From 82918a5c31c64767fbbcf3b77332564e193b4134 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 10 Oct 2018 12:31:16 -0700 Subject: [PATCH 01/38] Add animation support --- interface/src/avatar/AvatarManager.cpp | 41 +++++++++++++++++++ interface/src/avatar/AvatarManager.h | 1 + .../src/avatars-renderer/Avatar.h | 14 +++++++ 3 files changed, 56 insertions(+) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index d31b201dc7..1f2c9e462d 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -83,10 +83,18 @@ AvatarManager::AvatarManager(QObject* parent) : const int AVATAR_TRANSIT_FRAME_COUNT = 11; // Based on testing const int AVATAR_TRANSIT_FRAMES_PER_METER = 1; // Based on testing + const QString START_ANIMATION_URL = "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx"; + const QString MIDDLE_ANIMATION_URL = "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx"; + const QString END_ANIMATION_URL = "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx"; + _transitConfig._totalFrames = AVATAR_TRANSIT_FRAME_COUNT; _transitConfig._triggerDistance = AVATAR_TRANSIT_TRIGGER_DISTANCE; _transitConfig._framesPerMeter = AVATAR_TRANSIT_FRAMES_PER_METER; _transitConfig._isDistanceBased = true; + + _transitConfig._startTransitAnimation = AvatarTransit::TransitAnimation(START_ANIMATION_URL, 0, 14); + _transitConfig._middleTransitAnimation = AvatarTransit::TransitAnimation(MIDDLE_ANIMATION_URL, 15, 0); + _transitConfig._endTransitAnimation = AvatarTransit::TransitAnimation(END_ANIMATION_URL, 16, 38); } AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { @@ -134,6 +142,39 @@ void AvatarManager::setSpace(workload::SpacePointer& space ) { _space = space; } +void AvatarManager::playTransitAnimations(AvatarTransit::Status status) { + auto startAnimation = _transitConfig._startTransitAnimation; + auto middleAnimation = _transitConfig._middleTransitAnimation; + auto endAnimation = _transitConfig._endTransitAnimation; + + const float REFERENCE_FPS = 30.0f; + switch (status) { + case AvatarTransit::Status::START_FRAME: + qDebug() << "START_FRAME"; + _myAvatar->overrideAnimation(startAnimation._animationUrl, REFERENCE_FPS, false, startAnimation._firstFrame, + startAnimation._firstFrame + startAnimation._frameCount); + break; + case AvatarTransit::Status::START_TRANSIT: + qDebug() << "START_TRANSIT"; + _myAvatar->overrideAnimation(middleAnimation._animationUrl, REFERENCE_FPS, false, middleAnimation._firstFrame, + middleAnimation._firstFrame + middleAnimation._frameCount); + break; + case AvatarTransit::Status::END_TRANSIT: + qDebug() << "END_TRANSIT"; + _myAvatar->overrideAnimation(endAnimation._animationUrl, REFERENCE_FPS, false, endAnimation._firstFrame, + endAnimation._firstFrame + endAnimation._frameCount); + break; + case AvatarTransit::Status::END_FRAME: + qDebug() << "END_FRAME"; + _myAvatar->restoreAnimation(); + break; + case AvatarTransit::Status::IDLE: + break; + case AvatarTransit::Status::TRANSITING: + break; + } +} + void AvatarManager::updateMyAvatar(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "AvatarManager::updateMyAvatar()"); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 209b976c44..0dc39e991b 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -213,6 +213,7 @@ private: // frequently grabs a read lock on the hash to get a given avatar by ID void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override; + void playTransitAnimations(AvatarTransit::Status status); QVector _avatarsToFade; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 1087f74c07..21e359051f 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -54,9 +54,11 @@ class AvatarTransit { public: enum Status { IDLE = 0, + START_FRAME, START_TRANSIT, TRANSITING, END_TRANSIT, + END_FRAME, ABORT_TRANSIT }; @@ -67,6 +69,15 @@ public: EASE_IN_OUT }; + struct TransitAnimation { + TransitAnimation(){}; + TransitAnimation(const QString& animationUrl, int firstFrame, int frameCount) : + _firstFrame(firstFrame), _frameCount(frameCount), _animationUrl(animationUrl){}; + int _firstFrame; + int _frameCount; + QString _animationUrl; + }; + struct TransitConfig { TransitConfig() {}; int _totalFrames { 0 }; @@ -74,6 +85,9 @@ public: bool _isDistanceBased { false }; float _triggerDistance { 0 }; EaseType _easeType { EaseType::EASE_OUT }; + TransitAnimation _startTransitAnimation; + TransitAnimation _middleTransitAnimation; + TransitAnimation _endTransitAnimation; }; AvatarTransit() {}; From 1adac78828403d0d26eb8e9ad7d0e8b76ee29b2c Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 11 Oct 2018 10:45:35 -0700 Subject: [PATCH 02/38] fix errors and set new anim parameters --- interface/src/avatar/AvatarManager.cpp | 19 +++++------- .../src/avatars-renderer/Avatar.cpp | 31 +++++++++++++------ .../src/avatars-renderer/Avatar.h | 5 ++- 3 files changed, 33 insertions(+), 22 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 1f2c9e462d..0bb1bc2e0a 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -81,7 +81,7 @@ AvatarManager::AvatarManager(QObject* parent) : const float AVATAR_TRANSIT_TRIGGER_DISTANCE = 1.0f; const int AVATAR_TRANSIT_FRAME_COUNT = 11; // Based on testing - const int AVATAR_TRANSIT_FRAMES_PER_METER = 1; // Based on testing + const float AVATAR_TRANSIT_FRAMES_PER_METER = 0.5f; // Based on testing const QString START_ANIMATION_URL = "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx"; const QString MIDDLE_ANIMATION_URL = "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx"; @@ -92,9 +92,9 @@ AvatarManager::AvatarManager(QObject* parent) : _transitConfig._framesPerMeter = AVATAR_TRANSIT_FRAMES_PER_METER; _transitConfig._isDistanceBased = true; - _transitConfig._startTransitAnimation = AvatarTransit::TransitAnimation(START_ANIMATION_URL, 0, 14); + _transitConfig._startTransitAnimation = AvatarTransit::TransitAnimation(START_ANIMATION_URL, 0, 10); _transitConfig._middleTransitAnimation = AvatarTransit::TransitAnimation(MIDDLE_ANIMATION_URL, 15, 0); - _transitConfig._endTransitAnimation = AvatarTransit::TransitAnimation(END_ANIMATION_URL, 16, 38); + _transitConfig._endTransitAnimation = AvatarTransit::TransitAnimation(END_ANIMATION_URL, 22, 49); } AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { @@ -180,8 +180,9 @@ void AvatarManager::updateMyAvatar(float deltaTime) { PerformanceWarning warn(showWarnings, "AvatarManager::updateMyAvatar()"); AvatarTransit::Status status = _myAvatar->updateTransit(deltaTime, _myAvatar->getNextPosition(), _transitConfig); - bool sendFirstTransitPackage = (status == AvatarTransit::Status::START_TRANSIT); - bool blockTransitData = (status == AvatarTransit::Status::TRANSITING); + if (status != AvatarTransit::Status::IDLE) { + playTransitAnimations(status); + } _myAvatar->update(deltaTime); render::Transaction transaction; @@ -191,13 +192,9 @@ void AvatarManager::updateMyAvatar(float deltaTime) { quint64 now = usecTimestampNow(); quint64 dt = now - _lastSendAvatarDataTime; - - if (sendFirstTransitPackage || (dt > MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS && !_myAvatarDataPacketsPaused && !blockTransitData)) { + if (dt > MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS && !_myAvatarDataPacketsPaused) { // send head/hand data to the avatar mixer and voxel server - PerformanceTimer perfTimer("send"); - if (sendFirstTransitPackage) { - _myAvatar->overrideNextPackagePositionData(_myAvatar->getTransit()->getEndPosition()); - } + PerformanceTimer perfTimer("send"); _myAvatar->sendAvatarDataPacket(); _lastSendAvatarDataTime = now; _myAvatarSendRate.increment(); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index f154746707..6ac808ec66 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -144,9 +144,12 @@ void AvatarTransit::start(float deltaTime, const glm::vec3& startPosition, const _totalDistance = glm::length(_transitLine); _easeType = config._easeType; const float REFERENCE_FRAMES_PER_SECOND = 30.0f; - + _preTime = (float)config._startTransitAnimation._frameCount / REFERENCE_FRAMES_PER_SECOND; + _postTime = (float)config._endTransitAnimation._frameCount / REFERENCE_FRAMES_PER_SECOND; + int transitFrames = (!config._isDistanceBased) ? config._totalFrames : config._framesPerMeter * _totalDistance; - _totalTime = (float)transitFrames / REFERENCE_FRAMES_PER_SECOND; + _transitTime = (float)transitFrames / REFERENCE_FRAMES_PER_SECOND; + _totalTime = _transitTime + _preTime + _postTime; _currentTime = 0.0f; _isTransiting = true; } @@ -173,19 +176,27 @@ AvatarTransit::Status AvatarTransit::updatePosition(float deltaTime) { Status status = Status::IDLE; if (_isTransiting) { float nextTime = _currentTime + deltaTime; - glm::vec3 newPosition; - if (nextTime >= _totalTime) { - _currentPosition = _endPosition; - _isTransiting = false; - status = Status::END_TRANSIT; - } else { + if (nextTime < _preTime) { + _currentPosition = _startPosition; if (_currentTime == 0) { + status = Status::START_FRAME; + } + } else if (nextTime < _totalTime - _postTime){ + if (_currentTime <= _preTime) { status = Status::START_TRANSIT; } else { + float percentageIntoTransit = (nextTime - _preTime) / _transitTime; + _currentPosition = _startPosition + getEaseValue(_easeType, percentageIntoTransit) * _transitLine; status = Status::TRANSITING; } - float percentageIntoTransit = nextTime / _totalTime; - _currentPosition = _startPosition + getEaseValue(_easeType, percentageIntoTransit) * _transitLine; + } else { + _currentPosition = _endPosition; + if (nextTime >= _totalTime) { + _isTransiting = false; + status = Status::END_FRAME; + } else if (_currentTime < _totalTime - _postTime) { + status = Status::END_TRANSIT; + } } _currentTime = nextTime; } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 21e359051f..ffe90ecaa2 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -81,7 +81,7 @@ public: struct TransitConfig { TransitConfig() {}; int _totalFrames { 0 }; - int _framesPerMeter { 0 }; + float _framesPerMeter { 0.0f }; bool _isDistanceBased { false }; float _triggerDistance { 0 }; EaseType _easeType { EaseType::EASE_OUT }; @@ -115,7 +115,10 @@ private: glm::vec3 _transitLine; float _totalDistance { 0.0f }; + float _preTime { 0.0f }; float _totalTime { 0.0f }; + float _transitTime { 0.0f }; + float _postTime { 0.0f }; float _currentTime { 0.0f }; EaseType _easeType { EaseType::EASE_OUT }; Status _status { Status::IDLE }; From 2f10327ad36cc5bcd81d55efcb832e2e29a5765c Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 11 Oct 2018 10:57:39 -0700 Subject: [PATCH 03/38] Fix last anim value --- interface/src/avatar/AvatarManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 0bb1bc2e0a..87c0bc471b 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -94,7 +94,7 @@ AvatarManager::AvatarManager(QObject* parent) : _transitConfig._startTransitAnimation = AvatarTransit::TransitAnimation(START_ANIMATION_URL, 0, 10); _transitConfig._middleTransitAnimation = AvatarTransit::TransitAnimation(MIDDLE_ANIMATION_URL, 15, 0); - _transitConfig._endTransitAnimation = AvatarTransit::TransitAnimation(END_ANIMATION_URL, 22, 49); + _transitConfig._endTransitAnimation = AvatarTransit::TransitAnimation(END_ANIMATION_URL, 22, 27); } AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { From de1b6e717fc8f8acb46e61697e9c14431feed210 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 11 Oct 2018 11:35:56 -0700 Subject: [PATCH 04/38] Cancel animation on chained trasits --- interface/src/avatar/AvatarManager.cpp | 2 +- .../src/avatars-renderer/Avatar.cpp | 15 +++++++-------- libraries/avatars/src/AvatarData.cpp | 2 +- libraries/avatars/src/AvatarData.h | 1 + 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 87c0bc471b..80e4e4ba66 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -313,7 +313,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { if (inView && avatar->hasNewJointData()) { numAvatarsUpdated++; } - auto transitStatus = avatar->_transit.update(deltaTime, avatar->_globalPosition, _transitConfig); + auto transitStatus = avatar->_transit.update(deltaTime, avatar->_lastPosition, _transitConfig); if (avatar->getIsNewAvatar() && (transitStatus == AvatarTransit::Status::START_TRANSIT || transitStatus == AvatarTransit::Status::ABORT_TRANSIT)) { avatar->_transit.reset(); avatar->setIsNewAvatar(false); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 6ac808ec66..53c2019138 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -114,19 +114,18 @@ void Avatar::setShowNamesAboveHeads(bool show) { } AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config) { - glm::vec3 currentPosition = _isTransiting ? _currentPosition : avatarPosition; - float oneFrameDistance = glm::length(currentPosition - _lastPosition); + float oneFrameDistance = _isTransiting ? glm::length(avatarPosition - _endPosition) : glm::length(avatarPosition - _lastPosition); const float MAX_TRANSIT_DISTANCE = 30.0f; float scaledMaxTransitDistance = MAX_TRANSIT_DISTANCE * _scale; - if (oneFrameDistance > config._triggerDistance && !_isTransiting) { + if (oneFrameDistance > config._triggerDistance) { if (oneFrameDistance < scaledMaxTransitDistance) { - start(deltaTime, _lastPosition, currentPosition, config); + start(deltaTime, _lastPosition, avatarPosition, config); } else { - _lastPosition = currentPosition; - return Status::ABORT_TRANSIT; + _lastPosition = avatarPosition; + _status = Status::ABORT_TRANSIT; } } - _lastPosition = currentPosition; + _lastPosition = avatarPosition; _status = updatePosition(deltaTime); return _status; } @@ -150,7 +149,7 @@ void AvatarTransit::start(float deltaTime, const glm::vec3& startPosition, const int transitFrames = (!config._isDistanceBased) ? config._totalFrames : config._framesPerMeter * _totalDistance; _transitTime = (float)transitFrames / REFERENCE_FRAMES_PER_SECOND; _totalTime = _transitTime + _preTime + _postTime; - _currentTime = 0.0f; + _currentTime = _isTransiting ? _preTime : 0.0f; _isTransiting = true; } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index d78d83bc09..a22cc4a1d3 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -896,7 +896,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { auto newValue = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]) + offset; if (_globalPosition != newValue) { - _globalPosition = newValue; + _lastPosition = _globalPosition = newValue; _globalPositionChanged = now; } sourceBuffer += sizeof(AvatarDataPacket::AvatarGlobalPosition); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 46489451f7..dcdaa70ad7 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -1378,6 +1378,7 @@ protected: // where Entities are located. This is currently only used by the mixer to decide how often to send // updates about one avatar to another. glm::vec3 _globalPosition { 0, 0, 0 }; + glm::vec3 _lastPosition { 0, 0, 0 }; glm::vec3 _globalPositionOverride { 0, 0, 0 }; bool _overrideGlobalPosition { false }; From 4883a60afc9326c016ef37f617d70c2bca27a13a Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 11 Oct 2018 18:55:14 -0700 Subject: [PATCH 05/38] entity list type filter WIP - still some style issues --- scripts/system/html/css/edit-style.css | 46 +++++++++-- scripts/system/html/entityList.html | 20 +++-- scripts/system/html/js/entityList.js | 109 +++++++++++++++++++------ 3 files changed, 136 insertions(+), 39 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 6c1931932a..fc055174fb 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -1031,30 +1031,60 @@ textarea:enabled[scrolling="true"]::-webkit-resizer { position: relative; /* New positioning context. */ } -#search-area { +#filter-area { padding-right: 168px; padding-bottom: 24px; } -#filter { - width: 98%; +#filter-type-multiselect { + position: relative; +} +#filter-type-selectBox { + position: absolute; +} +#filter-type-selectBox select { + font-weight: bold; + font-family: FiraSans-SemiBold; + color: #404040; + background-color: #afafaf; +} +#filter-type-checkboxes { + position: absolute; + top: 20px; + display: none; + border: 1px #dadada solid; +} +#filter-type-checkboxes label { + display: block; + font-family: FiraSans-SemiBold; + color: #404040; + background-color: #afafaf; +} +#filter-type-checkboxes label:hover { + background-color: #1e90ff; } -#in-view { +#filter-search-and-icon { + position: absolute; + left: 100px; + width: 60%; +} + +#filter-in-view { position: absolute; right: 126px; } -#radius-and-unit { +#filter-radius-and-unit { + position: relative; float: right; margin-right: -168px; - position: relative; top: -17px; } -#radius-and-unit label { +#filter-radius-and-unit label { margin-left: 2px; } -#radius-and-unit input { +#filter-radius-and-unit input { width: 120px; } diff --git a/scripts/system/html/entityList.html b/scripts/system/html/entityList.html index 7eed78ecf3..8bcdc37d64 100644 --- a/scripts/system/html/entityList.html +++ b/scripts/system/html/entityList.html @@ -30,12 +30,22 @@
-
- Y - -
+
+
+
+ +
+
+ +
+
+
+ Y +
+ +
- +
diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index fed4dfb632..6b23d703ee 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -59,6 +59,18 @@ const COMPARE_DESCENDING = function(a, b) { return COMPARE_ASCENDING(b, a); } +const FILTER_TYPES = [ + "Shape", + "Model", + "Image", + "Light", + "Zone", + "Web", + "Material", + "ParticleEffect", + "Text", +]; + // List of all entities var entities = [] // List of all entities, indexed by Entity ID @@ -72,6 +84,7 @@ var entityList = null; // The ListView var currentSortColumn = 'type'; var currentSortOrder = ASCENDING_SORT; +var typeFilters = []; var isFilterInView = false; var showExtraInfo = false; @@ -105,9 +118,11 @@ function loaded() { elToggleLocked = document.getElementById("locked"); elToggleVisible = document.getElementById("visible"); elDelete = document.getElementById("delete"); - elFilter = document.getElementById("filter"); - elInView = document.getElementById("in-view") - elRadius = document.getElementById("radius"); + elFilterTypeSelectBox = document.getElementById("filter-type-selectBox"); + elFilterTypeCheckboxes = document.getElementById("filter-type-checkboxes"); + elFilterSearch = document.getElementById("filter-search"); + elFilterInView = document.getElementById("filter-in-view") + elFilterRadius = document.getElementById("filter-radius"); elExport = document.getElementById("export"); elPal = document.getElementById("pal"); elInfoToggle = document.getElementById("info-toggle"); @@ -171,13 +186,33 @@ function loaded() { elDelete.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' })); } - elFilter.onkeyup = refreshEntityList; - elFilter.onpaste = refreshEntityList; - elFilter.onchange = onFilterChange; - elFilter.onblur = refreshFooter; - elInView.onclick = toggleFilterInView; - elRadius.onchange = onRadiusChange; + elFilterSearch.onkeyup = refreshEntityList; + elFilterSearch.onpaste = refreshEntityList; + elFilterSearch.onchange = onFilterChange; + elFilterSearch.onblur = refreshFooter; + elFilterInView.onclick = toggleFilterInView; + elFilterRadius.onchange = onRadiusChange; elInfoToggle.onclick = toggleInfo; + + // create filter type dropdown checkboxes w/ label for each type + elFilterTypeSelectBox.onclick = toggleTypeDropdown; + for (let i = 0; i < FILTER_TYPES.length; ++i) { + let type = FILTER_TYPES[i]; + let typeFilterID = "filter-type-" + type; + let elDiv = document.createElement('div'); + let elLabel = document.createElement('label'); + elLabel.setAttribute("for", typeFilterID); + elLabel.innerText = type; + let elInput = document.createElement('input'); + elInput.setAttribute("type", "checkbox"); + elInput.setAttribute("id", typeFilterID); + elInput.checked = true; // all types are checked initially + toggleTypeFilter(type, false); // add all types to the initial type filter + elInput.onclick = onToggleTypeFilter(type); + elDiv.appendChild(elInput); + elDiv.appendChild(elLabel); + elFilterTypeCheckboxes.appendChild(elDiv); + } elNoEntitiesInView.style.display = "none"; @@ -301,17 +336,16 @@ function loaded() { function refreshEntityList() { PROFILE("refresh-entity-list", function() { PROFILE("filter", function() { - let searchTerm = elFilter.value.toLowerCase(); - if (searchTerm === '') { - visibleEntities = entities.slice(0); - } else { - visibleEntities = entities.filter(function(e) { - return e.name.toLowerCase().indexOf(searchTerm) > -1 - || e.type.toLowerCase().indexOf(searchTerm) > -1 - || e.fullUrl.toLowerCase().indexOf(searchTerm) > -1 - || e.id.toLowerCase().indexOf(searchTerm) > -1; - }); - } + let searchTerm = elFilterSearch.value.toLowerCase(); + visibleEntities = entities.filter(function(e) { + let type = e.type === "Box" || e.type === "Sphere" ? "Shape" : e.type; + let typeFilter = typeFilters.indexOf(type) > -1; + let searchFilter = searchTerm === '' || (e.name.toLowerCase().indexOf(searchTerm) > -1 || + e.type.toLowerCase().indexOf(searchTerm) > -1 || + e.fullUrl.toLowerCase().indexOf(searchTerm) > -1 || + e.id.toLowerCase().indexOf(searchTerm) > -1); + return typeFilter && searchFilter; + }); }); PROFILE("sort", function() { @@ -588,10 +622,10 @@ function loaded() { function toggleFilterInView() { isFilterInView = !isFilterInView; if (isFilterInView) { - elInView.setAttribute(FILTER_IN_VIEW_ATTRIBUTE, FILTER_IN_VIEW_ATTRIBUTE); + elFilterInView.setAttribute(FILTER_IN_VIEW_ATTRIBUTE, FILTER_IN_VIEW_ATTRIBUTE); elNoEntitiesInView.style.display = "inline"; } else { - elInView.removeAttribute(FILTER_IN_VIEW_ATTRIBUTE); + elFilterInView.removeAttribute(FILTER_IN_VIEW_ATTRIBUTE); elNoEntitiesInView.style.display = "none"; } EventBridge.emitWebEvent(JSON.stringify({ type: "filterInView", filterInView: isFilterInView })); @@ -604,12 +638,34 @@ function loaded() { } function onRadiusChange() { - elRadius.value = Math.max(elRadius.value, 0); - elNoEntitiesRadius.firstChild.nodeValue = elRadius.value; + elFilterRadius.value = Math.max(elFilterRadius.value, 0); + elNoEntitiesRadius.firstChild.nodeValue = elFilterRadius.value; elNoEntitiesMessage.style.display = "none"; - EventBridge.emitWebEvent(JSON.stringify({ type: 'radius', radius: elRadius.value })); + EventBridge.emitWebEvent(JSON.stringify({ type: 'radius', radius: elFilterRadius.value })); refreshEntities(); } + + function toggleTypeDropdown() { + elFilterTypeCheckboxes.style.display = elFilterTypeCheckboxes.style.display === "block" ? "none" : "block"; + } + + function toggleTypeFilter(type, refresh) { + let typeFilterIndex = typeFilters.indexOf(type); + if (typeFilterIndex > -1) { + typeFilters.splice(typeFilterIndex, 1); + } else { + typeFilters.push(type); + } + if (refresh) { + refreshEntityList(); + } + } + + function onToggleTypeFilter(type) { + return function() { + toggleTypeFilter(type, true); + }; + } function toggleInfo(event) { showExtraInfo = !showExtraInfo; @@ -623,7 +679,7 @@ function loaded() { entityList.resize(); event.stopPropagation(); } - + document.addEventListener("keydown", function (keyDownEvent) { if (keyDownEvent.target.nodeName === "INPUT") { return; @@ -675,6 +731,7 @@ function loaded() { refreshSortOrder(); refreshEntities(); }); + augmentSpinButtons(); From 97438a08d931da46505830deaf6529943983ac9d Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 11 Oct 2018 23:04:33 -0700 Subject: [PATCH 06/38] styling fixes, add icon, adjust Types text, click outside to close --- scripts/system/html/css/edit-style.css | 80 ++++++++---- scripts/system/html/entityList.html | 7 +- scripts/system/html/js/entityList.js | 168 +++++++++++++++---------- 3 files changed, 165 insertions(+), 90 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index fc055174fb..da0d806a41 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -1036,38 +1036,74 @@ textarea:enabled[scrolling="true"]::-webkit-resizer { padding-bottom: 24px; } -#filter-type-multiselect { - position: relative; +#filter-area .multiselect { + position: relative; } -#filter-type-selectBox { - position: absolute; +#filter-area .selectBox { + position: absolute; } -#filter-type-selectBox select { - font-weight: bold; - font-family: FiraSans-SemiBold; - color: #404040; - background-color: #afafaf; +#filter-area .selectBox select { + font-family: FiraSans-SemiBold; + font-size: 15px; + color: #afafaf; + background-color: #252525; + border: none; + height: 28px; + width: 107px; + text-align-last: center; +} +#filter-area .overSelect { + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; } #filter-type-checkboxes { - position: absolute; - top: 20px; - display: none; - border: 1px #dadada solid; + position: absolute; + z-index: 2; + top: 15px; + display: none; + border: none; +} +#filter-type-checkboxes div { + height: 19px; +} +#filter-type-checkboxes span { + font-family: hifi-glyphs; + font-size: 16px; + color: #404040; + padding: 12px 12px; + vertical-align: middle; } #filter-type-checkboxes label { - display: block; - font-family: FiraSans-SemiBold; - color: #404040; - background-color: #afafaf; + display: block; + font-family: FiraSans-SemiBold; + color: #404040; + background-color: #afafaf; + padding: 0 20px; + vertical-align: middle; } #filter-type-checkboxes label:hover { - background-color: #1e90ff; + background-color: #1e90ff; +} +#filter-type-checkboxes input[type=checkbox] { + display: block; + position: relative; + top: 17px; + right: -10px; +} +#filter-type-checkboxes input[type=checkbox] + label { + background-image: none; +} +#filter-type-checkboxes input[type=checkbox]:checked + label { + background-image: none; } #filter-search-and-icon { - position: absolute; - left: 100px; - width: 60%; + position: absolute; + left: 120px; + width: calc(100% - 300px); } #filter-in-view { @@ -1076,7 +1112,7 @@ textarea:enabled[scrolling="true"]::-webkit-resizer { } #filter-radius-and-unit { - position: relative; + position: relative; float: right; margin-right: -168px; top: -17px; diff --git a/scripts/system/html/entityList.html b/scripts/system/html/entityList.html index 8bcdc37d64..e301f36945 100644 --- a/scripts/system/html/entityList.html +++ b/scripts/system/html/entityList.html @@ -31,9 +31,12 @@
-
+
- + +
diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 6b23d703ee..8812594408 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -60,17 +60,29 @@ const COMPARE_DESCENDING = function(a, b) { } const FILTER_TYPES = [ - "Shape", - "Model", - "Image", - "Light", - "Zone", - "Web", - "Material", - "ParticleEffect", - "Text", + "Shape", + "Model", + "Image", + "Light", + "Zone", + "Web", + "Material", + "ParticleEffect", + "Text", ]; +const ICON_FOR_TYPE = { + Shape: "n", + Model: "", + Image: "", + Light: "p", + Zone: "o", + Web: "q", + Material: "", + ParticleEffect: "", + Text: "l", +}; + // List of all entities var entities = [] // List of all entities, indexed by Entity ID @@ -118,8 +130,9 @@ function loaded() { elToggleLocked = document.getElementById("locked"); elToggleVisible = document.getElementById("visible"); elDelete = document.getElementById("delete"); - elFilterTypeSelectBox = document.getElementById("filter-type-selectBox"); - elFilterTypeCheckboxes = document.getElementById("filter-type-checkboxes"); + elFilterTypeSelectBox = document.getElementById("filter-type-selectBox"); + elFilterTypeText = document.getElementById("filter-type-text"); + elFilterTypeCheckboxes = document.getElementById("filter-type-checkboxes"); elFilterSearch = document.getElementById("filter-search"); elFilterInView = document.getElementById("filter-in-view") elFilterRadius = document.getElementById("filter-radius"); @@ -132,6 +145,8 @@ function loaded() { elNoEntitiesInView = document.getElementById("no-entities-in-view"); elNoEntitiesRadius = document.getElementById("no-entities-radius"); + document.body.onclick = onBodyClick; + document.getElementById("entity-name").onclick = function() { setSortColumn('name'); }; @@ -193,26 +208,30 @@ function loaded() { elFilterInView.onclick = toggleFilterInView; elFilterRadius.onchange = onRadiusChange; elInfoToggle.onclick = toggleInfo; - - // create filter type dropdown checkboxes w/ label for each type - elFilterTypeSelectBox.onclick = toggleTypeDropdown; - for (let i = 0; i < FILTER_TYPES.length; ++i) { - let type = FILTER_TYPES[i]; - let typeFilterID = "filter-type-" + type; - let elDiv = document.createElement('div'); - let elLabel = document.createElement('label'); - elLabel.setAttribute("for", typeFilterID); - elLabel.innerText = type; - let elInput = document.createElement('input'); - elInput.setAttribute("type", "checkbox"); - elInput.setAttribute("id", typeFilterID); - elInput.checked = true; // all types are checked initially - toggleTypeFilter(type, false); // add all types to the initial type filter - elInput.onclick = onToggleTypeFilter(type); - elDiv.appendChild(elInput); - elDiv.appendChild(elLabel); - elFilterTypeCheckboxes.appendChild(elDiv); - } + + // create filter type dropdown checkboxes w/ label for each type + elFilterTypeSelectBox.onclick = toggleTypeDropdown; + for (let i = 0; i < FILTER_TYPES.length; ++i) { + let type = FILTER_TYPES[i]; + let typeFilterID = "filter-type-" + type; + let elDiv = document.createElement('div'); + let elLabel = document.createElement('label'); + elLabel.setAttribute("for", typeFilterID); + elLabel.innerText = type; + let elSpan = document.createElement('span'); + elSpan.setAttribute("class", "typeIcon"); + elSpan.innerHTML = ICON_FOR_TYPE[type]; + let elInput = document.createElement('input'); + elInput.setAttribute("type", "checkbox"); + elInput.setAttribute("id", typeFilterID); + elInput.checked = true; // all types are checked initially + toggleTypeFilter(type, false); // add all types to the initial type filter + elInput.onclick = onToggleTypeFilter(type); + elDiv.appendChild(elInput); + elLabel.insertBefore(elSpan, elLabel.childNodes[0]); + elDiv.appendChild(elLabel); + elFilterTypeCheckboxes.appendChild(elDiv); + } elNoEntitiesInView.style.display = "none"; @@ -336,16 +355,16 @@ function loaded() { function refreshEntityList() { PROFILE("refresh-entity-list", function() { PROFILE("filter", function() { - let searchTerm = elFilterSearch.value.toLowerCase(); - visibleEntities = entities.filter(function(e) { - let type = e.type === "Box" || e.type === "Sphere" ? "Shape" : e.type; - let typeFilter = typeFilters.indexOf(type) > -1; - let searchFilter = searchTerm === '' || (e.name.toLowerCase().indexOf(searchTerm) > -1 || - e.type.toLowerCase().indexOf(searchTerm) > -1 || - e.fullUrl.toLowerCase().indexOf(searchTerm) > -1 || - e.id.toLowerCase().indexOf(searchTerm) > -1); - return typeFilter && searchFilter; - }); + let searchTerm = elFilterSearch.value.toLowerCase(); + visibleEntities = entities.filter(function(e) { + let type = e.type === "Box" || e.type === "Sphere" ? "Shape" : e.type; + let typeFilter = typeFilters.indexOf(type) > -1; + let searchFilter = searchTerm === '' || (e.name.toLowerCase().indexOf(searchTerm) > -1 || + e.type.toLowerCase().indexOf(searchTerm) > -1 || + e.fullUrl.toLowerCase().indexOf(searchTerm) > -1 || + e.id.toLowerCase().indexOf(searchTerm) > -1); + return typeFilter && searchFilter; + }); }); PROFILE("sort", function() { @@ -644,29 +663,38 @@ function loaded() { EventBridge.emitWebEvent(JSON.stringify({ type: 'radius', radius: elFilterRadius.value })); refreshEntities(); } - - function toggleTypeDropdown() { - elFilterTypeCheckboxes.style.display = elFilterTypeCheckboxes.style.display === "block" ? "none" : "block"; - } - - function toggleTypeFilter(type, refresh) { - let typeFilterIndex = typeFilters.indexOf(type); - if (typeFilterIndex > -1) { - typeFilters.splice(typeFilterIndex, 1); - } else { - typeFilters.push(type); - } - if (refresh) { - refreshEntityList(); - } - } - - function onToggleTypeFilter(type) { - return function() { - toggleTypeFilter(type, true); - }; - } - + + function toggleTypeDropdown() { + elFilterTypeCheckboxes.style.display = elFilterTypeCheckboxes.style.display === "block" ? "none" : "block"; + } + + function toggleTypeFilter(type, refresh) { + let typeFilterIndex = typeFilters.indexOf(type); + if (typeFilterIndex > -1) { + typeFilters.splice(typeFilterIndex, 1); + } else { + typeFilters.push(type); + } + + if (typeFilters.length === 0) { + elFilterTypeText.innerText = "No Types"; + } else if (typeFilters.length === FILTER_TYPES.length) { + elFilterTypeText.innerText = "All Types"; + } else { + elFilterTypeText.innerText = "Types..."; + } + + if (refresh) { + refreshEntityList(); + } + } + + function onToggleTypeFilter(type) { + return function() { + toggleTypeFilter(type, true); + }; + } + function toggleInfo(event) { showExtraInfo = !showExtraInfo; if (showExtraInfo) { @@ -679,7 +707,15 @@ function loaded() { entityList.resize(); event.stopPropagation(); } - + + function onBodyClick(event) { + let targetNode = event.target; + if (!elFilterTypeSelectBox.contains(targetNode) && !elFilterTypeCheckboxes.contains(targetNode) && + elFilterTypeCheckboxes.style.display === "block") { + toggleTypeDropdown(); + } + } + document.addEventListener("keydown", function (keyDownEvent) { if (keyDownEvent.target.nodeName === "INPUT") { return; @@ -731,7 +767,7 @@ function loaded() { refreshSortOrder(); refreshEntities(); }); - + augmentSpinButtons(); From 52ed6cb6e95299c578b2c3803ed42959c192d040 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Fri, 12 Oct 2018 11:35:51 -0700 Subject: [PATCH 07/38] Abort settle animation if already moving --- interface/src/avatar/AvatarManager.cpp | 6 +++++- .../avatars-renderer/src/avatars-renderer/Avatar.cpp | 8 +++++++- libraries/avatars-renderer/src/avatars-renderer/Avatar.h | 2 ++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 80e4e4ba66..5fb9e0e0af 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -168,6 +168,10 @@ void AvatarManager::playTransitAnimations(AvatarTransit::Status status) { qDebug() << "END_FRAME"; _myAvatar->restoreAnimation(); break; + case AvatarTransit::Status::PRE_TRANSIT_IDLE: + break; + case AvatarTransit::Status::POST_TRANSIT_IDLE: + break; case AvatarTransit::Status::IDLE: break; case AvatarTransit::Status::TRANSITING: @@ -180,7 +184,7 @@ void AvatarManager::updateMyAvatar(float deltaTime) { PerformanceWarning warn(showWarnings, "AvatarManager::updateMyAvatar()"); AvatarTransit::Status status = _myAvatar->updateTransit(deltaTime, _myAvatar->getNextPosition(), _transitConfig); - if (status != AvatarTransit::Status::IDLE) { + if (status != AvatarTransit::Status::IDLE && status != AvatarTransit::Status::PRE_TRANSIT_IDLE && status != AvatarTransit::Status::POST_TRANSIT_IDLE) { playTransitAnimations(status); } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 53c2019138..d8587b6086 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -127,6 +127,10 @@ AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& av } _lastPosition = avatarPosition; _status = updatePosition(deltaTime); + if (_isTransiting && oneFrameDistance > 0.1f && _status == Status::POST_TRANSIT_IDLE) { + reset(); + _status = Status::END_FRAME; + } return _status; } @@ -177,18 +181,20 @@ AvatarTransit::Status AvatarTransit::updatePosition(float deltaTime) { float nextTime = _currentTime + deltaTime; if (nextTime < _preTime) { _currentPosition = _startPosition; + status = Status::PRE_TRANSIT_IDLE; if (_currentTime == 0) { status = Status::START_FRAME; } } else if (nextTime < _totalTime - _postTime){ + status = Status::TRANSITING; if (_currentTime <= _preTime) { status = Status::START_TRANSIT; } else { float percentageIntoTransit = (nextTime - _preTime) / _transitTime; _currentPosition = _startPosition + getEaseValue(_easeType, percentageIntoTransit) * _transitLine; - status = Status::TRANSITING; } } else { + status = Status::POST_TRANSIT_IDLE; _currentPosition = _endPosition; if (nextTime >= _totalTime) { _isTransiting = false; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index ffe90ecaa2..fe163b9dae 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -55,9 +55,11 @@ public: enum Status { IDLE = 0, START_FRAME, + PRE_TRANSIT_IDLE, START_TRANSIT, TRANSITING, END_TRANSIT, + POST_TRANSIT_IDLE, END_FRAME, ABORT_TRANSIT }; From 6320f9bab7f6a34fde523e9544c75dbd83b0919e Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 12 Oct 2018 11:39:54 -0700 Subject: [PATCH 08/38] tweaks --- scripts/system/html/css/edit-style.css | 26 ++++++++++++++++-------- scripts/system/html/js/entityList.js | 28 +++++++++++++++----------- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index da0d806a41..8179b95e35 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -1036,13 +1036,13 @@ textarea:enabled[scrolling="true"]::-webkit-resizer { padding-bottom: 24px; } -#filter-area .multiselect { +.multiselect { position: relative; } -#filter-area .selectBox { +.selectBox { position: absolute; } -#filter-area .selectBox select { +.selectBox select { font-family: FiraSans-SemiBold; font-size: 15px; color: #afafaf; @@ -1052,22 +1052,24 @@ textarea:enabled[scrolling="true"]::-webkit-resizer { width: 107px; text-align-last: center; } -#filter-area .overSelect { +.overSelect { position: absolute; left: 0; right: 0; top: 0; bottom: 0; } + #filter-type-checkboxes { position: absolute; z-index: 2; - top: 15px; + top: 28px; display: none; border: none; } #filter-type-checkboxes div { - height: 19px; + position: relative; + height: 25px; } #filter-type-checkboxes span { font-family: hifi-glyphs; @@ -1077,21 +1079,26 @@ textarea:enabled[scrolling="true"]::-webkit-resizer { vertical-align: middle; } #filter-type-checkboxes label { + position: relative; + top: -13px; + z-index: 3; display: block; font-family: FiraSans-SemiBold; color: #404040; background-color: #afafaf; padding: 0 20px; + height: 25px; vertical-align: middle; } #filter-type-checkboxes label:hover { background-color: #1e90ff; } #filter-type-checkboxes input[type=checkbox] { - display: block; position: relative; - top: 17px; + top: 4px; right: -10px; + z-index: 4; + display: block; } #filter-type-checkboxes input[type=checkbox] + label { background-image: none; @@ -1099,6 +1106,9 @@ textarea:enabled[scrolling="true"]::-webkit-resizer { #filter-type-checkboxes input[type=checkbox]:checked + label { background-image: none; } +#filter-type-checkboxes input[type=checkbox]:hover + label { + background-color: #1e90ff; +} #filter-search-and-icon { position: absolute; diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 8812594408..f780fd0e2e 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -146,7 +146,6 @@ function loaded() { elNoEntitiesRadius = document.getElementById("no-entities-radius"); document.body.onclick = onBodyClick; - document.getElementById("entity-name").onclick = function() { setSortColumn('name'); }; @@ -209,7 +208,7 @@ function loaded() { elFilterRadius.onchange = onRadiusChange; elInfoToggle.onclick = toggleInfo; - // create filter type dropdown checkboxes w/ label for each type + // create filter type dropdown checkboxes with label and icon for each type elFilterTypeSelectBox.onclick = toggleTypeDropdown; for (let i = 0; i < FILTER_TYPES.length; ++i) { let type = FILTER_TYPES[i]; @@ -225,7 +224,7 @@ function loaded() { elInput.setAttribute("type", "checkbox"); elInput.setAttribute("id", typeFilterID); elInput.checked = true; // all types are checked initially - toggleTypeFilter(type, false); // add all types to the initial type filter + toggleTypeFilter(type, false); // add all types to the initial types filter elInput.onclick = onToggleTypeFilter(type); elDiv.appendChild(elInput); elLabel.insertBefore(elSpan, elLabel.childNodes[0]); @@ -664,8 +663,12 @@ function loaded() { refreshEntities(); } + function isTypeDropdownVisible() { + return elFilterTypeCheckboxes.style.display === "block"; + } + function toggleTypeDropdown() { - elFilterTypeCheckboxes.style.display = elFilterTypeCheckboxes.style.display === "block" ? "none" : "block"; + elFilterTypeCheckboxes.style.display = isTypeDropdownVisible() ? "none" : "block"; } function toggleTypeFilter(type, refresh) { @@ -695,6 +698,15 @@ function loaded() { }; } + function onBodyClick(event) { + // if clicking anywhere outside of the type filter dropdown and it's open then close it + let elTarget = event.target; + if (isTypeDropdownVisible() && !elFilterTypeSelectBox.contains(elTarget) && + !elFilterTypeCheckboxes.contains(elTarget)) { + toggleTypeDropdown(); + } + } + function toggleInfo(event) { showExtraInfo = !showExtraInfo; if (showExtraInfo) { @@ -707,14 +719,6 @@ function loaded() { entityList.resize(); event.stopPropagation(); } - - function onBodyClick(event) { - let targetNode = event.target; - if (!elFilterTypeSelectBox.contains(targetNode) && !elFilterTypeCheckboxes.contains(targetNode) && - elFilterTypeCheckboxes.style.display === "block") { - toggleTypeDropdown(); - } - } document.addEventListener("keydown", function (keyDownEvent) { if (keyDownEvent.target.nodeName === "INPUT") { From a85336044f0201afa66fd287e5d9cdca13a99b1c Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 12 Oct 2018 18:03:47 -0700 Subject: [PATCH 09/38] add X to clear button to filter search --- scripts/system/html/css/edit-style.css | 14 +++++++++++++- scripts/system/html/entityList.html | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 8179b95e35..69fed02099 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -198,7 +198,7 @@ td.url { } -input[type="text"], input[type="number"], textarea { +input[type="text"], input[type="search"], input[type="number"], textarea { margin: 0; padding: 0 12px; color: #afafaf; @@ -257,6 +257,18 @@ input[type="text"] { width: 100%; } +input[type="search"] { + height: 28px; + width: 100%; +} +input[type="search"]::-webkit-search-cancel-button { + -webkit-appearance: none; + height: 20px; + width: 20px; + border-radius:10px; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH4goNAQIFbBwsbwAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAZfSURBVDgRAVQGq/kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9PT0YAwMDBgAAAAD8/Pz5+vr67MrKyv0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAA+Pj4KAgICQgAAAE3///9RAQEBFQAAAAD////pAQEBu/39/ab+/v7BxcXF9gAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAADs7OzMEBASIAQEBRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAACm+/v7cMXFxewAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAPT09OwEBAagBAQEcAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8AAAAA2f///2XCwsLDAAAAAAAAAAABAAAAAAAAAAA9PT0KAwMDt////z4AAAAAAAAAAAEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAcIBAQFJvr6+9gAAAAACAAAAAAAAAAAAAABg////PgEBAQAAAAAAS0tLADg4OAAAAAAAAAAAAP///wADAwMAQEBAACEhIQD///8A////AP7+/j76+vpWAAAAAAAAAAACAAAAAD09PQ8CAgJkAQEBAP///wD///8ACgoKAFhYWAAyMjIAAAAAAAICAgBGRkYAT09PABEREQAAAAAAAAAAAAAAAAACAgJwOjo6EAAAAAAEAAAAAAICAg8BAQExAAAAAAEBAQABAQEAsrKyAAoKCgBaWloA9/f3ABsbGwBISEgAtra2AM7OzgACAgIA////AP///wABAQEuBQUFDgAAAPAEAAAAAPz8/BkEBAQAAQEBAAAAAAAAAAAA+vr6AKioqAALCwsAZWVlAAcHBwC/v78Au7u7AAEBAQD///8AAAAAAAAAAAAAAAABAAAAAAAAAAACAAAAAAQEBOgBAQEAAQEBAAEBAQABAQEAAQEBAPz8/ADT09MADg4OAP39/QDQ0NAA/v7+AP///wAAAAAAAAAAAAEBAQABAQEAAQEBAAAAAAACAAAAAAAAAP8AAAD/AAAAAAAAAAAAAAAAAAAAACkpKQBQUFAAx8fHAObm5gBfX18AFxcXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAP39/fz+/v7z////AP///wD///8AJycnAGFhYQDc3NwApaWlAJaWlgD29vYAZmZmABQUFAACAgIAAQEBAAEBAQABAQH1AAAA/AAAAAACAAAAAPr6+ukBAQGkAAAAAAAAAAABAQEAQEBAAObm5gCmpqYA+fn5APPz8wCdnZ0A////ACwsLAD///8AAAAAAAAAAAD///+k9vb26QAAAAABAAAAAAAAAAA+Pj4uAgICxgAAAAsAAAAAEBAQAPr6+gD29vYAAAAAAAAAAAABAQEAAgICAP///wD+/v4AAAAAAAAAAPL8/Pw/xMTE0AAAAAACAAAAAAAAAAD5+fnV////nQICAgABAQEA8fHxAPX19QABAQEAAAAAAAAAAAD///8A/v7+AP7+/gAAAAAAAAAAAP7+/p36+vrSAAAAAAAAAAADAAAAAAAAAADl5eX/ICAgwQAAAA////8q////BgEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD1/f39mAEBAXrGxsb7AAAAAAAAAAADAAAAAAAAAAAAAAAA4eHh/BgYGLsBAQHDBAQEHAAAACP///8AAQEBAAAAAAAAAAAAAAAA+////7QBAQFu+fn5m8bGxvoAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPz8/Cv7+/iUBAQFMAgICEQICAgD8/PzdAwMDs/j4+OvHx8f5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8TUnpZ7EwQgAAAABJRU5ErkJggg==') +} + input[type="number"] { position: relative; height: 28px; diff --git a/scripts/system/html/entityList.html b/scripts/system/html/entityList.html index e301f36945..626402362d 100644 --- a/scripts/system/html/entityList.html +++ b/scripts/system/html/entityList.html @@ -43,7 +43,7 @@
- Y + Y
From 978cd4bb7764cb4bbc017da1164043d0f509d75c Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 12 Oct 2018 18:57:26 -0700 Subject: [PATCH 10/38] styling tweaks, fix X clear search not refreshing --- scripts/system/html/css/edit-style.css | 15 ++++++++------- scripts/system/html/entityList.html | 2 +- scripts/system/html/js/entityList.js | 9 +-------- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 69fed02099..699c27448a 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -265,7 +265,6 @@ input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none; height: 20px; width: 20px; - border-radius:10px; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH4goNAQIFbBwsbwAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAZfSURBVDgRAVQGq/kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9PT0YAwMDBgAAAAD8/Pz5+vr67MrKyv0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAA+Pj4KAgICQgAAAE3///9RAQEBFQAAAAD////pAQEBu/39/ab+/v7BxcXF9gAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAADs7OzMEBASIAQEBRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAACm+/v7cMXFxewAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAPT09OwEBAagBAQEcAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8AAAAA2f///2XCwsLDAAAAAAAAAAABAAAAAAAAAAA9PT0KAwMDt////z4AAAAAAAAAAAEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAcIBAQFJvr6+9gAAAAACAAAAAAAAAAAAAABg////PgEBAQAAAAAAS0tLADg4OAAAAAAAAAAAAP///wADAwMAQEBAACEhIQD///8A////AP7+/j76+vpWAAAAAAAAAAACAAAAAD09PQ8CAgJkAQEBAP///wD///8ACgoKAFhYWAAyMjIAAAAAAAICAgBGRkYAT09PABEREQAAAAAAAAAAAAAAAAACAgJwOjo6EAAAAAAEAAAAAAICAg8BAQExAAAAAAEBAQABAQEAsrKyAAoKCgBaWloA9/f3ABsbGwBISEgAtra2AM7OzgACAgIA////AP///wABAQEuBQUFDgAAAPAEAAAAAPz8/BkEBAQAAQEBAAAAAAAAAAAA+vr6AKioqAALCwsAZWVlAAcHBwC/v78Au7u7AAEBAQD///8AAAAAAAAAAAAAAAABAAAAAAAAAAACAAAAAAQEBOgBAQEAAQEBAAEBAQABAQEAAQEBAPz8/ADT09MADg4OAP39/QDQ0NAA/v7+AP///wAAAAAAAAAAAAEBAQABAQEAAQEBAAAAAAACAAAAAAAAAP8AAAD/AAAAAAAAAAAAAAAAAAAAACkpKQBQUFAAx8fHAObm5gBfX18AFxcXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAP39/fz+/v7z////AP///wD///8AJycnAGFhYQDc3NwApaWlAJaWlgD29vYAZmZmABQUFAACAgIAAQEBAAEBAQABAQH1AAAA/AAAAAACAAAAAPr6+ukBAQGkAAAAAAAAAAABAQEAQEBAAObm5gCmpqYA+fn5APPz8wCdnZ0A////ACwsLAD///8AAAAAAAAAAAD///+k9vb26QAAAAABAAAAAAAAAAA+Pj4uAgICxgAAAAsAAAAAEBAQAPr6+gD29vYAAAAAAAAAAAABAQEAAgICAP///wD+/v4AAAAAAAAAAPL8/Pw/xMTE0AAAAAACAAAAAAAAAAD5+fnV////nQICAgABAQEA8fHxAPX19QABAQEAAAAAAAAAAAD///8A/v7+AP7+/gAAAAAAAAAAAP7+/p36+vrSAAAAAAAAAAADAAAAAAAAAADl5eX/ICAgwQAAAA////8q////BgEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD1/f39mAEBAXrGxsb7AAAAAAAAAAADAAAAAAAAAAAAAAAA4eHh/BgYGLsBAQHDBAQEHAAAACP///8AAQEBAAAAAAAAAAAAAAAA+////7QBAQFu+fn5m8bGxvoAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPz8/Cv7+/iUBAQFMAgICEQICAgD8/PzdAwMDs/j4+OvHx8f5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8TUnpZ7EwQgAAAABJRU5ErkJggg==') } @@ -1084,11 +1083,13 @@ textarea:enabled[scrolling="true"]::-webkit-resizer { height: 25px; } #filter-type-checkboxes span { + position: relative; + top: 3px; font-family: hifi-glyphs; font-size: 16px; color: #404040; - padding: 12px 12px; - vertical-align: middle; + padding-left: 12px; + padding-right: 12px; } #filter-type-checkboxes label { position: relative; @@ -1098,16 +1099,16 @@ textarea:enabled[scrolling="true"]::-webkit-resizer { font-family: FiraSans-SemiBold; color: #404040; background-color: #afafaf; - padding: 0 20px; - height: 25px; - vertical-align: middle; + padding-top: 1px; + padding-right: 12px; + height: 24px; } #filter-type-checkboxes label:hover { background-color: #1e90ff; } #filter-type-checkboxes input[type=checkbox] { position: relative; - top: 4px; + top: 6px; right: -10px; z-index: 4; display: block; diff --git a/scripts/system/html/entityList.html b/scripts/system/html/entityList.html index 626402362d..06c2be8e73 100644 --- a/scripts/system/html/entityList.html +++ b/scripts/system/html/entityList.html @@ -39,7 +39,7 @@
- +
diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index f780fd0e2e..0f3f27a547 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -201,9 +201,7 @@ function loaded() { EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' })); } elFilterSearch.onkeyup = refreshEntityList; - elFilterSearch.onpaste = refreshEntityList; - elFilterSearch.onchange = onFilterChange; - elFilterSearch.onblur = refreshFooter; + elFilterSearch.onsearch = refreshEntityList; elFilterInView.onclick = toggleFilterInView; elFilterRadius.onchange = onRadiusChange; elInfoToggle.onclick = toggleInfo; @@ -650,11 +648,6 @@ function loaded() { refreshEntities(); } - function onFilterChange() { - refreshEntityList(); - entityList.resize(); - } - function onRadiusChange() { elFilterRadius.value = Math.max(elFilterRadius.value, 0); elNoEntitiesRadius.firstChild.nodeValue = elFilterRadius.value; From ad09007bd8f20f62618871b105bd288551534d26 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Sat, 13 Oct 2018 16:32:51 -0700 Subject: [PATCH 11/38] Fix flickering holded entity, renames and refactors --- interface/src/avatar/AvatarActionHold.cpp | 4 +- interface/src/avatar/AvatarManager.cpp | 26 ++++--- interface/src/avatar/AvatarManager.h | 2 +- .../src/avatars-renderer/Avatar.cpp | 73 +++++++++---------- .../src/avatars-renderer/Avatar.h | 20 +++-- 5 files changed, 64 insertions(+), 61 deletions(-) diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index 230f8aa64b..53074ac4ba 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -135,7 +135,7 @@ bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm:: glm::vec3 palmPosition; glm::quat palmRotation; - bool isTransitingWithAvatar = holdingAvatar->getTransit()->isTransiting(); + bool isTransitingWithAvatar = holdingAvatar->getTransit()->isActive(); if (isTransitingWithAvatar != _isTransitingWithAvatar) { _isTransitingWithAvatar = isTransitingWithAvatar; auto ownerEntity = _ownerEntity.lock(); @@ -424,7 +424,7 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { if (ownerEntity) { ownerEntity->setDynamicDataDirty(true); ownerEntity->setDynamicDataNeedsTransmit(true); - ownerEntity->setTransitingWithAvatar(myAvatar->getTransit()->isTransiting()); + ownerEntity->setTransitingWithAvatar(myAvatar->getTransit()->isActive()); } }); } diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 5fb9e0e0af..c1fddaa680 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -142,14 +142,14 @@ void AvatarManager::setSpace(workload::SpacePointer& space ) { _space = space; } -void AvatarManager::playTransitAnimations(AvatarTransit::Status status) { +void AvatarManager::handleTransitAnimations(AvatarTransit::Status status) { auto startAnimation = _transitConfig._startTransitAnimation; auto middleAnimation = _transitConfig._middleTransitAnimation; auto endAnimation = _transitConfig._endTransitAnimation; const float REFERENCE_FPS = 30.0f; switch (status) { - case AvatarTransit::Status::START_FRAME: + case AvatarTransit::Status::STARTED: qDebug() << "START_FRAME"; _myAvatar->overrideAnimation(startAnimation._animationUrl, REFERENCE_FPS, false, startAnimation._firstFrame, startAnimation._firstFrame + startAnimation._frameCount); @@ -164,18 +164,20 @@ void AvatarManager::playTransitAnimations(AvatarTransit::Status status) { _myAvatar->overrideAnimation(endAnimation._animationUrl, REFERENCE_FPS, false, endAnimation._firstFrame, endAnimation._firstFrame + endAnimation._frameCount); break; - case AvatarTransit::Status::END_FRAME: + case AvatarTransit::Status::ENDED: qDebug() << "END_FRAME"; _myAvatar->restoreAnimation(); break; - case AvatarTransit::Status::PRE_TRANSIT_IDLE: + case AvatarTransit::Status::PRE_TRANSIT: break; - case AvatarTransit::Status::POST_TRANSIT_IDLE: + case AvatarTransit::Status::POST_TRANSIT: break; case AvatarTransit::Status::IDLE: break; case AvatarTransit::Status::TRANSITING: break; + case AvatarTransit::Status::ABORT_TRANSIT: + break; } } @@ -184,9 +186,10 @@ void AvatarManager::updateMyAvatar(float deltaTime) { PerformanceWarning warn(showWarnings, "AvatarManager::updateMyAvatar()"); AvatarTransit::Status status = _myAvatar->updateTransit(deltaTime, _myAvatar->getNextPosition(), _transitConfig); - if (status != AvatarTransit::Status::IDLE && status != AvatarTransit::Status::PRE_TRANSIT_IDLE && status != AvatarTransit::Status::POST_TRANSIT_IDLE) { - playTransitAnimations(status); - } + handleTransitAnimations(status); + + bool sendFirstTransitPacket = (status == AvatarTransit::Status::STARTED); + bool sendCurrentTransitPacket = (status != AvatarTransit::Status::IDLE && (status != AvatarTransit::Status::POST_TRANSIT || status != AvatarTransit::Status::ENDED)); _myAvatar->update(deltaTime); render::Transaction transaction; @@ -196,9 +199,14 @@ void AvatarManager::updateMyAvatar(float deltaTime) { quint64 now = usecTimestampNow(); quint64 dt = now - _lastSendAvatarDataTime; - if (dt > MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS && !_myAvatarDataPacketsPaused) { + if (sendFirstTransitPacket || (dt > MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS && !_myAvatarDataPacketsPaused)) { // send head/hand data to the avatar mixer and voxel server PerformanceTimer perfTimer("send"); + if (sendFirstTransitPacket) { + _myAvatar->overrideNextPacketPositionData(_myAvatar->getTransit()->getEndPosition()); + } else if (sendCurrentTransitPacket) { + _myAvatar->overrideNextPacketPositionData(_myAvatar->getTransit()->getCurrentPosition()); + } _myAvatar->sendAvatarDataPacket(); _lastSendAvatarDataTime = now; _myAvatarSendRate.increment(); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 0dc39e991b..0f82aa7329 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -213,7 +213,7 @@ private: // frequently grabs a read lock on the hash to get a given avatar by ID void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override; - void playTransitAnimations(AvatarTransit::Status status); + void handleTransitAnimations(AvatarTransit::Status status); QVector _avatarsToFade; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index d8587b6086..b2747277c9 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -114,10 +114,12 @@ void Avatar::setShowNamesAboveHeads(bool show) { } AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config) { - float oneFrameDistance = _isTransiting ? glm::length(avatarPosition - _endPosition) : glm::length(avatarPosition - _lastPosition); - const float MAX_TRANSIT_DISTANCE = 30.0f; - float scaledMaxTransitDistance = MAX_TRANSIT_DISTANCE * _scale; - if (oneFrameDistance > config._triggerDistance) { + bool checkDistance = (!_isActive || (_status == Status::POST_TRANSIT || _status == Status::ENDED)); + float oneFrameDistance = _isActive ? glm::length(avatarPosition - _endPosition) : glm::length(avatarPosition - _lastPosition); + + const float TRANSIT_TRIGGER_MAX_DISTANCE = 30.0f; + float scaledMaxTransitDistance = TRANSIT_TRIGGER_MAX_DISTANCE * _scale; + if (oneFrameDistance > config._triggerDistance && checkDistance) { if (oneFrameDistance < scaledMaxTransitDistance) { start(deltaTime, _lastPosition, avatarPosition, config); } else { @@ -127,9 +129,12 @@ AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& av } _lastPosition = avatarPosition; _status = updatePosition(deltaTime); - if (_isTransiting && oneFrameDistance > 0.1f && _status == Status::POST_TRANSIT_IDLE) { + + const float SETTLE_ABORT_DISTANCE = 0.1f; + float scaledSettleAbortDistance = SETTLE_ABORT_DISTANCE * _scale; + if (_isActive && oneFrameDistance > scaledSettleAbortDistance && _status == Status::POST_TRANSIT) { reset(); - _status = Status::END_FRAME; + _status = Status::ENDED; } return _status; } @@ -137,7 +142,7 @@ AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& av void AvatarTransit::reset() { _lastPosition = _endPosition; _currentPosition = _endPosition; - _isTransiting = false; + _isActive = false; } void AvatarTransit::start(float deltaTime, const glm::vec3& startPosition, const glm::vec3& endPosition, const AvatarTransit::TransitConfig& config) { _startPosition = startPosition; @@ -147,14 +152,14 @@ void AvatarTransit::start(float deltaTime, const glm::vec3& startPosition, const _totalDistance = glm::length(_transitLine); _easeType = config._easeType; const float REFERENCE_FRAMES_PER_SECOND = 30.0f; - _preTime = (float)config._startTransitAnimation._frameCount / REFERENCE_FRAMES_PER_SECOND; - _postTime = (float)config._endTransitAnimation._frameCount / REFERENCE_FRAMES_PER_SECOND; + _preTransitTime = (float)config._startTransitAnimation._frameCount / REFERENCE_FRAMES_PER_SECOND; + _postTransitTime = (float)config._endTransitAnimation._frameCount / REFERENCE_FRAMES_PER_SECOND; int transitFrames = (!config._isDistanceBased) ? config._totalFrames : config._framesPerMeter * _totalDistance; _transitTime = (float)transitFrames / REFERENCE_FRAMES_PER_SECOND; - _totalTime = _transitTime + _preTime + _postTime; - _currentTime = _isTransiting ? _preTime : 0.0f; - _isTransiting = true; + _totalTime = _transitTime + _preTransitTime + _postTransitTime; + _currentTime = _isActive ? _preTransitTime : 0.0f; + _isActive = true; } float AvatarTransit::getEaseValue(AvatarTransit::EaseType type, float value) { @@ -177,29 +182,29 @@ float AvatarTransit::getEaseValue(AvatarTransit::EaseType type, float value) { AvatarTransit::Status AvatarTransit::updatePosition(float deltaTime) { Status status = Status::IDLE; - if (_isTransiting) { + if (_isActive) { float nextTime = _currentTime + deltaTime; - if (nextTime < _preTime) { + if (nextTime < _preTransitTime) { _currentPosition = _startPosition; - status = Status::PRE_TRANSIT_IDLE; + status = Status::PRE_TRANSIT; if (_currentTime == 0) { - status = Status::START_FRAME; + status = Status::STARTED; } - } else if (nextTime < _totalTime - _postTime){ + } else if (nextTime < _totalTime - _postTransitTime){ status = Status::TRANSITING; - if (_currentTime <= _preTime) { + if (_currentTime <= _preTransitTime) { status = Status::START_TRANSIT; } else { - float percentageIntoTransit = (nextTime - _preTime) / _transitTime; + float percentageIntoTransit = (nextTime - _preTransitTime) / _transitTime; _currentPosition = _startPosition + getEaseValue(_easeType, percentageIntoTransit) * _transitLine; } } else { - status = Status::POST_TRANSIT_IDLE; + status = Status::POST_TRANSIT; _currentPosition = _endPosition; if (nextTime >= _totalTime) { - _isTransiting = false; - status = Status::END_FRAME; - } else if (_currentTime < _totalTime - _postTime) { + _isActive = false; + status = Status::ENDED; + } else if (_currentTime < _totalTime - _postTransitTime) { status = Status::END_TRANSIT; } } @@ -208,11 +213,6 @@ AvatarTransit::Status AvatarTransit::updatePosition(float deltaTime) { return status; } -bool AvatarTransit::getNextPosition(glm::vec3& nextPosition) { - nextPosition = _currentPosition; - return _isTransiting; -} - Avatar::Avatar(QThread* thread) : _voiceSphereID(GeometryCache::UNKNOWN_ID) { @@ -554,14 +554,11 @@ void Avatar::relayJointDataToChildren() { void Avatar::simulate(float deltaTime, bool inView) { PROFILE_RANGE(simulation, "simulate"); - if (_transit.isTransiting()) { - glm::vec3 nextPosition; - if (_transit.getNextPosition(nextPosition)) { - _globalPosition = nextPosition; - _globalPositionChanged = usecTimestampNow(); - if (!hasParent()) { - setLocalPosition(nextPosition); - } + if (_transit.isActive()) { + _globalPosition = _transit.getCurrentPosition(); + _globalPositionChanged = usecTimestampNow(); + if (!hasParent()) { + setLocalPosition(_transit.getCurrentPosition()); } } @@ -575,7 +572,7 @@ void Avatar::simulate(float deltaTime, bool inView) { PROFILE_RANGE(simulation, "updateJoints"); if (inView) { Head* head = getHead(); - if (_hasNewJointData || _transit.isTransiting()) { + if (_hasNewJointData || _transit.isActive()) { _skeletonModel->getRig().copyJointsFromJointData(_jointData); glm::mat4 rootTransform = glm::scale(_skeletonModel->getScale()) * glm::translate(_skeletonModel->getOffset()); _skeletonModel->getRig().computeExternalPoses(rootTransform); @@ -2006,7 +2003,7 @@ void Avatar::setTransitScale(float scale) { return _transit.setScale(scale); } -void Avatar::overrideNextPackagePositionData(const glm::vec3& position) { +void Avatar::overrideNextPacketPositionData(const glm::vec3& position) { std::lock_guard lock(_transitLock); _overrideGlobalPosition = true; _globalPositionOverride = position; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index fe163b9dae..e4fd667c1f 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -54,13 +54,13 @@ class AvatarTransit { public: enum Status { IDLE = 0, - START_FRAME, - PRE_TRANSIT_IDLE, + STARTED, + PRE_TRANSIT, START_TRANSIT, TRANSITING, END_TRANSIT, - POST_TRANSIT_IDLE, - END_FRAME, + POST_TRANSIT, + ENDED, ABORT_TRANSIT }; @@ -95,11 +95,9 @@ public: AvatarTransit() {}; Status update(float deltaTime, const glm::vec3& avatarPosition, const TransitConfig& config); Status getStatus() { return _status; } - bool isTransiting() { return _isTransiting; } + bool isActive() { return _isActive; } glm::vec3 getCurrentPosition() { return _currentPosition; } - bool getNextPosition(glm::vec3& nextPosition); glm::vec3 getEndPosition() { return _endPosition; } - float getTransitTime() { return _totalTime; } void setScale(float scale) { _scale = scale; } void reset(); @@ -107,7 +105,7 @@ private: Status updatePosition(float deltaTime); void start(float deltaTime, const glm::vec3& startPosition, const glm::vec3& endPosition, const TransitConfig& config); float getEaseValue(AvatarTransit::EaseType type, float value); - bool _isTransiting { false }; + bool _isActive { false }; glm::vec3 _startPosition; glm::vec3 _endPosition; @@ -117,10 +115,10 @@ private: glm::vec3 _transitLine; float _totalDistance { 0.0f }; - float _preTime { 0.0f }; + float _preTransitTime { 0.0f }; float _totalTime { 0.0f }; float _transitTime { 0.0f }; - float _postTime { 0.0f }; + float _postTransitTime { 0.0f }; float _currentTime { 0.0f }; EaseType _easeType { EaseType::EASE_OUT }; Status _status { Status::IDLE }; @@ -452,7 +450,7 @@ public: AvatarTransit::Status updateTransit(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config); void setTransitScale(float scale); - void overrideNextPackagePositionData(const glm::vec3& position); + void overrideNextPacketPositionData(const glm::vec3& position); signals: void targetScaleChanged(float targetScale); From 738ac30f1dc880f1dd3c903554282b543176be12 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 15 Oct 2018 11:16:30 -0700 Subject: [PATCH 12/38] Update default entity properties in Create --- scripts/system/edit.js | 215 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 203 insertions(+), 12 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 27858722ec..96d3d763b0 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -42,6 +42,9 @@ var TITLE_OFFSET = 60; var CREATE_TOOLS_WIDTH = 490; var MAX_DEFAULT_ENTITY_LIST_HEIGHT = 942; +var IMAGE_MODEL = "https://hifi-content.s3.amazonaws.com/DomainContent/production/default-image-model.fbx"; +var DEFAULT_IMAGE = "https://hifi-content.s3.amazonaws.com/DomainContent/production/no-image.jpg"; + var createToolsWindow = new CreateWindow( Script.resourcesPath() + "qml/hifi/tablet/EditTools.qml", 'Create Tools', @@ -294,6 +297,187 @@ function checkEditPermissionsAndUpdate() { } } +const DEFAULT_ENTITY_PROPERTIES = { + All: { + collisionless: true, + description: "", + rotation: { x: 0, y: 0, z: 0, w: 1 }, + collidesWith: "static,dynamic,kinematic,otherAvatar", + collisionSoundURL: "", + cloneable: false, + ignoreIK: true, + canCastShadow: true, + href: "", + script: "", + serverScripts:"", + velocity: { + x: 0, + y: 0, + z: 0 + }, + damping: 0, + angularVelocity: { + x: 0, + y: 0, + z: 0 + }, + angularDamping: 0, + restitution: 0.5, + friction: 0.5, + density: 1000, + gravity: { + x: 0, + y: 0, + z: 0 + }, + acceleration: { + x: 0, + y: 0, + z: 0 + }, + dynamic: false, + }, + Shape: { + shape: "Box", + dimensions: { x: 0.2, y: 0.2, z: 0.2 }, + color: { red: 0, green: 180, blue: 239 }, + }, + Text: { + text: "Text", + textColor: { red: 255, green: 255, blue: 255 }, + backgroundColor: { red: 0, green: 0, blue: 0 }, + lineHeight: 0.06, + faceCamera: false, + }, + Zone: { + flyingAllowed: true, + ghostingAllowed: true, + filter: "", + keyLightMode: "inherit", + keyLightColor: { red: 255, green: 255, blue: 255 }, + keyLight: { + intensity: 1.0, + direction: { + x: 0.0, + y: Math.PI / 4, + }, + castShadows: true + }, + ambientLightMode: "inherit", + ambientLight: { + ambientIntensity: 0.5, + ambientURL: "" + }, + hazeMode: "inherit", + haze: { + hazeRange: 1000, + hazeAltitudeEffect: false, + hazeBaseRef: 0, + hazeColor: { + red: 128, + green: 154, + blue: 129 + }, + hazeBackgroundBlend: 0, + hazeEnableGlare: false, + hazeGlareColor: { + red: 255, + green: 229, + blue: 179 + }, + }, + bloomMode: "inherit" + }, + Model: { + modelURL: "", + collisionShape: "none", + compoundShapeURL: "", + animation: { + url: "", + running: false, + allowTranslation: false, + loop: true, + hold: false, + currentFrame: 0, + firstFrame: 0, + lastFrame: 100000, + fps: 30.0, + } + }, + Image: { + dimensions: { + x: 0.5385, + y: 0.2819, + z: 0.0092 + }, + shapeType: "box", + collisionless: true, + modelURL: IMAGE_MODEL, + textures: JSON.stringify({ "tex.picture": DEFAULT_IMAGE }) + }, + Web: { + sourceUrl: "https://highfidelity.com/", + dpi: 30, + }, + Particles: { + lifespan: 1.5, + maxParticles: 10, + textures: "https://content.highfidelity.com/DomainContent/production/Particles/wispy-smoke.png", + emitRate: 5.5, + emitSpeed: 0, + speedSpread: 0, + emitDimensions: { x: 0, y: 0, z: 0 }, + emitOrientation: { x: 0, y: 0, z: 0, w: 1 }, + emitterShouldTrail: true, + particleRadius: 0.25, + radiusStart: 0, + radiusFinish: 0.1, + radiusSpread: 0, + particleColor: { + red: 255, + green: 255, + blue: 255 + }, + colorSpread: { + red: 0, + green: 0, + blue: 0 + }, + alpha: 0, + alphaStart: 1, + alphaFinish: 0, + alphaSpread: 0, + emitAcceleration: { + x: 0, + y: 2.5, + z: 0 + }, + accelerationSpread: { + x: 0, + y: 0, + z: 0 + }, + particleSpin: 0, + spinStart: 0, + spinFinish: 0, + spinSpread: 0, + rotateWithEntity: false, + azimuthStart: 0, + azimuthFinish: 0, + polarStart: -Math.PI, + polarFinish: Math.PI + }, + Light: { + color: { red: 255, green: 255, blue: 255 }, + intensity: 5.0, + falloffRadius: 1.0, + isSpotlight: false, + exponent: 1.0, + cutoff: 75.0, + dimensions: { x: 20, y: 20, z: 20 }, + }, +}; + var toolBar = (function () { var EDIT_SETTING = "io.highfidelity.isEditing"; // for communication with other scripts var that = {}, @@ -303,11 +487,29 @@ var toolBar = (function () { dialogWindow = null, tablet = null; + function applyProperties(originalProperties, newProperties) { + for (var key in newProperties) { + originalProperties[key] = newProperties[key]; + } + } function createNewEntity(properties) { var dimensions = properties.dimensions ? properties.dimensions : DEFAULT_DIMENSIONS; var position = getPositionToCreateEntity(); var entityID = null; + applyProperties(properties, DEFAULT_ENTITY_PROPERTIES.All); + + var type = properties.type; + if (type == "Box" || type == "Sphere") { + applyProperties(properties, DEFAULT_ENTITY_PROPERTIES.Shape); + } else if (type == "Image") { + properties.type = "Model"; + applyProperties(properties, DEFAULT_ENTITY_PROPERTIES.Image); + } else { + applyProperties(properties, DEFAULT_ENTITY_PROPERTIES[type]); + } + + if (position !== null && position !== undefined) { var direction; if (Camera.mode === "entity" || Camera.mode === "independent") { @@ -712,19 +914,8 @@ var toolBar = (function () { }); addButton("newImageButton", function () { - var IMAGE_MODEL = "https://hifi-content.s3.amazonaws.com/DomainContent/production/default-image-model.fbx"; - var DEFAULT_IMAGE = "https://hifi-content.s3.amazonaws.com/DomainContent/production/no-image.jpg"; createNewEntity({ - type: "Model", - dimensions: { - x: 0.5385, - y: 0.2819, - z: 0.0092 - }, - shapeType: "box", - collisionless: true, - modelURL: IMAGE_MODEL, - textures: JSON.stringify({ "tex.picture": DEFAULT_IMAGE }) + type: "Image", }); }); From 7da5fa9ea7fcba68b9c4ac46af3d558c5a0b3cba Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Tue, 16 Oct 2018 11:33:58 -0700 Subject: [PATCH 13/38] Transit animations won't play, they just get sent over the network --- interface/src/avatar/AvatarManager.cpp | 8 +- interface/src/avatar/MyAvatar.cpp | 17 ++++ interface/src/avatar/MyAvatar.h | 2 + libraries/animation/src/Rig.cpp | 120 +++++++++++++++++++++++-- libraries/animation/src/Rig.h | 8 ++ 5 files changed, 146 insertions(+), 9 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index c1fddaa680..373ae9980a 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -151,22 +151,22 @@ void AvatarManager::handleTransitAnimations(AvatarTransit::Status status) { switch (status) { case AvatarTransit::Status::STARTED: qDebug() << "START_FRAME"; - _myAvatar->overrideAnimation(startAnimation._animationUrl, REFERENCE_FPS, false, startAnimation._firstFrame, + _myAvatar->overrideNetworkAnimation(startAnimation._animationUrl, REFERENCE_FPS, false, startAnimation._firstFrame, startAnimation._firstFrame + startAnimation._frameCount); break; case AvatarTransit::Status::START_TRANSIT: qDebug() << "START_TRANSIT"; - _myAvatar->overrideAnimation(middleAnimation._animationUrl, REFERENCE_FPS, false, middleAnimation._firstFrame, + _myAvatar->overrideNetworkAnimation(middleAnimation._animationUrl, REFERENCE_FPS, false, middleAnimation._firstFrame, middleAnimation._firstFrame + middleAnimation._frameCount); break; case AvatarTransit::Status::END_TRANSIT: qDebug() << "END_TRANSIT"; - _myAvatar->overrideAnimation(endAnimation._animationUrl, REFERENCE_FPS, false, endAnimation._firstFrame, + _myAvatar->overrideNetworkAnimation(endAnimation._animationUrl, REFERENCE_FPS, false, endAnimation._firstFrame, endAnimation._firstFrame + endAnimation._frameCount); break; case AvatarTransit::Status::ENDED: qDebug() << "END_FRAME"; - _myAvatar->restoreAnimation(); + _myAvatar->restoreNetworkAnimation(); break; case AvatarTransit::Status::PRE_TRANSIT: break; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b347963cf1..152215e717 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1124,6 +1124,15 @@ void MyAvatar::overrideAnimation(const QString& url, float fps, bool loop, float _skeletonModel->getRig().overrideAnimation(url, fps, loop, firstFrame, lastFrame); } +void MyAvatar::overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "overrideNetworkAnimation", Q_ARG(const QString&, url), Q_ARG(float, fps), + Q_ARG(bool, loop), Q_ARG(float, firstFrame), Q_ARG(float, lastFrame)); + return; + } + _skeletonModel->getRig().overrideNetworkAnimation(url, fps, loop, firstFrame, lastFrame); +} + void MyAvatar::restoreAnimation() { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "restoreAnimation"); @@ -1132,6 +1141,14 @@ void MyAvatar::restoreAnimation() { _skeletonModel->getRig().restoreAnimation(); } +void MyAvatar::restoreNetworkAnimation() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "restoreNetworkAnimation"); + return; + } + _skeletonModel->getRig().restoreNetworkAnimation(); +} + QStringList MyAvatar::getAnimationRoles() { if (QThread::currentThread() != thread()) { QStringList result; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 16b765711a..9770a5bb1a 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -376,6 +376,7 @@ public: * }, 3000); */ Q_INVOKABLE void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); + Q_INVOKABLE void overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); /**jsdoc * The avatar animation system includes a set of default animations along with rules for how those animations are blended together with @@ -392,6 +393,7 @@ public: * }, 3000); */ Q_INVOKABLE void restoreAnimation(); + Q_INVOKABLE void restoreNetworkAnimation(); /**jsdoc * Each avatar has an avatar-animation.json file that defines which animations are used and how they are blended together with procedural data diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 91d4e0f9d3..b9e654964a 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -133,6 +133,43 @@ void Rig::overrideAnimation(const QString& url, float fps, bool loop, float firs _animVars.set("userAnimB", clipNodeEnum == UserAnimState::B); } +void Rig::overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { + UserAnimState::ClipNodeEnum clipNodeEnum; + if (_networkAnimState.clipNodeEnum == UserAnimState::None || _networkAnimState.clipNodeEnum == UserAnimState::B) { + clipNodeEnum = UserAnimState::A; + } + else { + clipNodeEnum = UserAnimState::B; + } + if (_networkNode) { + // find an unused AnimClip clipNode + _sendNetworkNode = true; + std::shared_ptr clip; + if (clipNodeEnum == UserAnimState::A) { + clip = std::dynamic_pointer_cast(_networkNode->findByName("userAnimA")); + } + else { + clip = std::dynamic_pointer_cast(_networkNode->findByName("userAnimB")); + } + if (clip) { + // set parameters + clip->setLoopFlag(loop); + clip->setStartFrame(firstFrame); + clip->setEndFrame(lastFrame); + const float REFERENCE_FRAMES_PER_SECOND = 30.0f; + float timeScale = fps / REFERENCE_FRAMES_PER_SECOND; + clip->setTimeScale(timeScale); + clip->loadURL(url); + } + } + // store current user anim state. + _networkAnimState = { clipNodeEnum, url, fps, loop, firstFrame, lastFrame }; + // notify the userAnimStateMachine the desired state. + _networkVars.set("userAnimNone", false); + _networkVars.set("userAnimA", clipNodeEnum == UserAnimState::A); + _networkVars.set("userAnimB", clipNodeEnum == UserAnimState::B); +} + void Rig::restoreAnimation() { if (_userAnimState.clipNodeEnum != UserAnimState::None) { _userAnimState.clipNodeEnum = UserAnimState::None; @@ -144,6 +181,17 @@ void Rig::restoreAnimation() { } } +void Rig::restoreNetworkAnimation() { + _sendNetworkNode = false; + if (_networkAnimState.clipNodeEnum != UserAnimState::None) { + _networkAnimState.clipNodeEnum = UserAnimState::None; + // notify the userAnimStateMachine the desired state. + _networkVars.set("userAnimNone", true); + _networkVars.set("userAnimA", false); + _networkVars.set("userAnimB", false); + } +} + QStringList Rig::getAnimationRoles() const { if (_animNode) { QStringList list; @@ -208,11 +256,17 @@ void Rig::restoreRoleAnimation(const QString& role) { void Rig::destroyAnimGraph() { _animSkeleton.reset(); _animLoader.reset(); + _networkLoader.reset(); _animNode.reset(); _internalPoseSet._relativePoses.clear(); _internalPoseSet._absolutePoses.clear(); _internalPoseSet._overridePoses.clear(); _internalPoseSet._overrideFlags.clear(); + _networkNode.reset(); + _networkPoseSet._relativePoses.clear(); + _networkPoseSet._absolutePoses.clear(); + _networkPoseSet._overridePoses.clear(); + _networkPoseSet._overrideFlags.clear(); _numOverrides = 0; _leftEyeJointChildren.clear(); _rightEyeJointChildren.clear(); @@ -229,14 +283,24 @@ void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOff _internalPoseSet._relativePoses.clear(); _internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); + _networkPoseSet._relativePoses.clear(); + _networkPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses); + buildAbsoluteRigPoses(_networkPoseSet._relativePoses, _networkPoseSet._absolutePoses); _internalPoseSet._overridePoses.clear(); _internalPoseSet._overridePoses = _animSkeleton->getRelativeDefaultPoses(); _internalPoseSet._overrideFlags.clear(); _internalPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints(), false); + + _networkPoseSet._overridePoses.clear(); + _networkPoseSet._overridePoses = _animSkeleton->getRelativeDefaultPoses(); + + _networkPoseSet._overrideFlags.clear(); + _networkPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints(), false); + _numOverrides = 0; buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses); @@ -270,6 +334,18 @@ void Rig::reset(const FBXGeometry& geometry) { _internalPoseSet._overrideFlags.clear(); _internalPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints(), false); + + _networkPoseSet._relativePoses.clear(); + _networkPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); + + buildAbsoluteRigPoses(_networkPoseSet._relativePoses, _networkPoseSet._absolutePoses); + + _networkPoseSet._overridePoses.clear(); + _networkPoseSet._overridePoses = _animSkeleton->getRelativeDefaultPoses(); + + _networkPoseSet._overrideFlags.clear(); + _networkPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints(), false); + _numOverrides = 0; buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses); @@ -1049,26 +1125,37 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons updateAnimationStateHandlers(); _animVars.setRigToGeometryTransform(_rigToGeometryTransform); - + if (_networkNode) { + _networkVars.setRigToGeometryTransform(_rigToGeometryTransform); + } AnimContext context(_enableDebugDrawIKTargets, _enableDebugDrawIKConstraints, _enableDebugDrawIKChains, getGeometryToRigTransform(), rigToWorldTransform); // evaluate the animation AnimVariantMap triggersOut; - + AnimVariantMap networkTriggersOut; _internalPoseSet._relativePoses = _animNode->evaluate(_animVars, context, deltaTime, triggersOut); + if (_networkNode) { + _networkPoseSet._relativePoses = _networkNode->evaluate(_networkVars, context, deltaTime, networkTriggersOut); + } if ((int)_internalPoseSet._relativePoses.size() != _animSkeleton->getNumJoints()) { // animations haven't fully loaded yet. _internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); } + if ((int)_networkPoseSet._relativePoses.size() != _animSkeleton->getNumJoints()) { + // animations haven't fully loaded yet. + _networkPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); + } _lastAnimVars = _animVars; _animVars.clearTriggers(); _animVars = triggersOut; + _networkVars.clearTriggers(); + _networkVars = networkTriggersOut; _lastContext = context; } applyOverridePoses(); buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses); - + buildAbsoluteRigPoses(_networkPoseSet._relativePoses, _networkPoseSet._absolutePoses); // copy internal poses to external poses { QWriteLocker writeLock(&_externalPoseSetLock); @@ -1707,9 +1794,12 @@ void Rig::initAnimGraph(const QUrl& url) { _animGraphURL = url; _animNode.reset(); + _networkNode.reset(); // load the anim graph _animLoader.reset(new AnimNodeLoader(url)); + _networkLoader.reset(new AnimNodeLoader(url)); + std::weak_ptr weakSkeletonPtr = _animSkeleton; connect(_animLoader.get(), &AnimNodeLoader::success, [this, weakSkeletonPtr](AnimNode::Pointer nodeIn) { _animNode = nodeIn; @@ -1740,6 +1830,26 @@ void Rig::initAnimGraph(const QUrl& url) { connect(_animLoader.get(), &AnimNodeLoader::error, [url](int error, QString str) { qCCritical(animation) << "Error loading" << url.toDisplayString() << "code = " << error << "str =" << str; }); + + connect(_networkLoader.get(), &AnimNodeLoader::success, [this, weakSkeletonPtr](AnimNode::Pointer nodeIn) { + _networkNode = nodeIn; + // abort load if the previous skeleton was deleted. + auto sharedSkeletonPtr = weakSkeletonPtr.lock(); + if (!sharedSkeletonPtr) { + return; + } + _networkNode->setSkeleton(sharedSkeletonPtr); + if (_networkAnimState.clipNodeEnum != UserAnimState::None) { + // restore the user animation we had before reset. + UserAnimState origState = _networkAnimState; + _networkAnimState = { UserAnimState::None, "", 30.0f, false, 0.0f, 0.0f }; + overrideNetworkAnimation(origState.url, origState.fps, origState.loop, origState.firstFrame, origState.lastFrame); + } + // emit onLoadComplete(); + }); + connect(_networkLoader.get(), &AnimNodeLoader::error, [url](int error, QString str) { + qCCritical(animation) << "Error loading" << url.toDisplayString() << "code = " << error << "str =" << str; + }); } } @@ -1817,13 +1927,13 @@ void Rig::copyJointsIntoJointData(QVector& jointDataVec) const { if (isIndexValid(i)) { // rotations are in absolute rig frame. glm::quat defaultAbsRot = geometryToRigPose.rot() * _animSkeleton->getAbsoluteDefaultPose(i).rot(); - data.rotation = _internalPoseSet._absolutePoses[i].rot(); + data.rotation = !_sendNetworkNode ? _internalPoseSet._absolutePoses[i].rot() : _networkPoseSet._absolutePoses[i].rot(); data.rotationIsDefaultPose = isEqual(data.rotation, defaultAbsRot); // translations are in relative frame but scaled so that they are in meters, // instead of geometry units. glm::vec3 defaultRelTrans = _geometryOffset.scale() * _animSkeleton->getRelativeDefaultPose(i).trans(); - data.translation = _geometryOffset.scale() * _internalPoseSet._relativePoses[i].trans(); + data.translation = _geometryOffset.scale() * (!_sendNetworkNode ? _internalPoseSet._relativePoses[i].trans() : _networkPoseSet._relativePoses[i].trans()); data.translationIsDefaultPose = isEqual(data.translation, defaultRelTrans); } else { data.translationIsDefaultPose = true; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 48f00d4e5d..e1012df029 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -113,7 +113,9 @@ public: void destroyAnimGraph(); void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); + void overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); void restoreAnimation(); + void restoreNetworkAnimation(); QStringList getAnimationRoles() const; void overrideRoleAnimation(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame); void restoreRoleAnimation(const QString& role); @@ -270,6 +272,7 @@ protected: // Only accessed by the main thread PoseSet _internalPoseSet; + PoseSet _networkPoseSet; // Copy of the _poseSet for external threads. PoseSet _externalPoseSet; @@ -301,9 +304,12 @@ protected: QUrl _animGraphURL; std::shared_ptr _animNode; + std::shared_ptr _networkNode; std::shared_ptr _animSkeleton; std::unique_ptr _animLoader; + std::unique_ptr _networkLoader; AnimVariantMap _animVars; + AnimVariantMap _networkVars; enum class RigRole { Idle = 0, @@ -350,6 +356,7 @@ protected: }; UserAnimState _userAnimState; + UserAnimState _networkAnimState; std::map _roleAnimStates; float _leftHandOverlayAlpha { 0.0f }; @@ -391,6 +398,7 @@ protected: int _rigId; bool _headEnabled { false }; + bool _sendNetworkNode { false }; AnimContext _lastContext; AnimVariantMap _lastAnimVars; From 078baa86e4fbe56cd095e2ea46c59abe0dc34e05 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Tue, 16 Oct 2018 13:16:01 -0700 Subject: [PATCH 14/38] Sitting abort post transit animation --- interface/src/avatar/AvatarManager.cpp | 2 +- libraries/animation/src/Rig.h | 1 + .../src/avatars-renderer/Avatar.cpp | 13 +++++++++++-- .../avatars-renderer/src/avatars-renderer/Avatar.h | 9 +++++++++ .../controllers/controllerModules/teleport.js | 2 ++ 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 373ae9980a..c3f6579e90 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -162,7 +162,7 @@ void AvatarManager::handleTransitAnimations(AvatarTransit::Status status) { case AvatarTransit::Status::END_TRANSIT: qDebug() << "END_TRANSIT"; _myAvatar->overrideNetworkAnimation(endAnimation._animationUrl, REFERENCE_FPS, false, endAnimation._firstFrame, - endAnimation._firstFrame + endAnimation._frameCount); + endAnimation._firstFrame + endAnimation._frameCount); break; case AvatarTransit::Status::ENDED: qDebug() << "END_FRAME"; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index e1012df029..37d1ec1dd3 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -116,6 +116,7 @@ public: void overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); void restoreAnimation(); void restoreNetworkAnimation(); + QStringList getAnimationRoles() const; void overrideRoleAnimation(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame); void restoreRoleAnimation(const QString& role); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index b2747277c9..b43e1c23f6 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -132,9 +132,12 @@ AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& av const float SETTLE_ABORT_DISTANCE = 0.1f; float scaledSettleAbortDistance = SETTLE_ABORT_DISTANCE * _scale; - if (_isActive && oneFrameDistance > scaledSettleAbortDistance && _status == Status::POST_TRANSIT) { + bool abortPostTransit = (_status == Status::POST_TRANSIT && _purpose == Purpose::SIT) || + (_isActive && oneFrameDistance > scaledSettleAbortDistance && _status == Status::POST_TRANSIT); + if (abortPostTransit) { reset(); _status = Status::ENDED; + _purpose = Purpose::UNDEFINED; } return _status; } @@ -203,6 +206,7 @@ AvatarTransit::Status AvatarTransit::updatePosition(float deltaTime) { _currentPosition = _endPosition; if (nextTime >= _totalTime) { _isActive = false; + _purpose = Purpose::UNDEFINED; status = Status::ENDED; } else if (_currentTime < _totalTime - _postTransitTime) { status = Status::END_TRANSIT; @@ -2000,7 +2004,12 @@ AvatarTransit::Status Avatar::updateTransit(float deltaTime, const glm::vec3& av void Avatar::setTransitScale(float scale) { std::lock_guard lock(_transitLock); - return _transit.setScale(scale); + _transit.setScale(scale); +} + +void Avatar::setTransitPurpose(int purpose) { + std::lock_guard lock(_transitLock); + _transit.setPurpose(static_cast(purpose)); } void Avatar::overrideNextPacketPositionData(const glm::vec3& position) { diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index e4fd667c1f..d375909609 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -71,6 +71,12 @@ public: EASE_IN_OUT }; + enum Purpose { + UNDEFINED = 0, + TELEPORT, + SIT + }; + struct TransitAnimation { TransitAnimation(){}; TransitAnimation(const QString& animationUrl, int firstFrame, int frameCount) : @@ -99,6 +105,7 @@ public: glm::vec3 getCurrentPosition() { return _currentPosition; } glm::vec3 getEndPosition() { return _endPosition; } void setScale(float scale) { _scale = scale; } + void setPurpose(const Purpose& purpose) { _purpose = purpose; } void reset(); private: @@ -123,6 +130,7 @@ private: EaseType _easeType { EaseType::EASE_OUT }; Status _status { Status::IDLE }; float _scale { 1.0f }; + Purpose _purpose { Purpose::UNDEFINED }; }; class Avatar : public AvatarData, public scriptable::ModelProvider { @@ -449,6 +457,7 @@ public: AvatarTransit::Status updateTransit(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config); void setTransitScale(float scale); + Q_INVOKABLE void setTransitPurpose(int purpose); void overrideNextPacketPositionData(const glm::vec3& position); diff --git a/scripts/system/controllers/controllerModules/teleport.js b/scripts/system/controllers/controllerModules/teleport.js index bf5022cdaf..d31f207b19 100644 --- a/scripts/system/controllers/controllerModules/teleport.js +++ b/scripts/system/controllers/controllerModules/teleport.js @@ -781,11 +781,13 @@ Script.include("/~/system/libraries/controllers.js"); if (target === TARGET.NONE || target === TARGET.INVALID) { // Do nothing } else if (target === TARGET.SEAT) { + MyAvatar.setTransitPurpose(2); Entities.callEntityMethod(result.objectID, 'sit'); } else if (target === TARGET.SURFACE || target === TARGET.DISCREPANCY) { var offset = getAvatarFootOffset(); result.intersection.y += offset; var shouldLandSafe = target === TARGET.DISCREPANCY; + MyAvatar.setTransitPurpose(1); MyAvatar.goToLocation(result.intersection, true, HMD.orientation, false, shouldLandSafe); HMD.centerUI(); MyAvatar.centerBody(); From a2aa63e9ed8ee169cef523c22de24f40abd52a9f Mon Sep 17 00:00:00 2001 From: David Back Date: Tue, 16 Oct 2018 14:28:52 -0700 Subject: [PATCH 15/38] adjust no entities message --- scripts/system/html/entityList.html | 2 +- scripts/system/html/js/entityList.js | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/scripts/system/html/entityList.html b/scripts/system/html/entityList.html index 06c2be8e73..f9b6fbd59b 100644 --- a/scripts/system/html/entityList.html +++ b/scripts/system/html/entityList.html @@ -108,7 +108,7 @@
- No entities found in view within a 100 meter radius. Try moving to a different location and refreshing. + There are no entities to display. Please check your filters or create an entity to begin.
diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 0f3f27a547..2b720f614d 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -142,8 +142,6 @@ function loaded() { elInfoToggleGlyph = elInfoToggle.firstChild; elFooter = document.getElementById("footer-text"); elNoEntitiesMessage = document.getElementById("no-entities"); - elNoEntitiesInView = document.getElementById("no-entities-in-view"); - elNoEntitiesRadius = document.getElementById("no-entities-radius"); document.body.onclick = onBodyClick; document.getElementById("entity-name").onclick = function() { @@ -230,8 +228,6 @@ function loaded() { elFilterTypeCheckboxes.appendChild(elDiv); } - elNoEntitiesInView.style.display = "none"; - entityList = new ListView(elEntityTableBody, elEntityTableScroll, elEntityTableHeaderRow, createRow, updateRow, clearRow, WINDOW_NONVARIABLE_HEIGHT); @@ -639,10 +635,8 @@ function loaded() { isFilterInView = !isFilterInView; if (isFilterInView) { elFilterInView.setAttribute(FILTER_IN_VIEW_ATTRIBUTE, FILTER_IN_VIEW_ATTRIBUTE); - elNoEntitiesInView.style.display = "inline"; } else { elFilterInView.removeAttribute(FILTER_IN_VIEW_ATTRIBUTE); - elNoEntitiesInView.style.display = "none"; } EventBridge.emitWebEvent(JSON.stringify({ type: "filterInView", filterInView: isFilterInView })); refreshEntities(); @@ -650,8 +644,6 @@ function loaded() { function onRadiusChange() { elFilterRadius.value = Math.max(elFilterRadius.value, 0); - elNoEntitiesRadius.firstChild.nodeValue = elFilterRadius.value; - elNoEntitiesMessage.style.display = "none"; EventBridge.emitWebEvent(JSON.stringify({ type: 'radius', radius: elFilterRadius.value })); refreshEntities(); } @@ -765,7 +757,6 @@ function loaded() { refreshEntities(); }); - augmentSpinButtons(); // Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked From 9cd3c35cc6858473c012e4f380b7d92f9a4381e1 Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 17 Oct 2018 18:18:51 -0700 Subject: [PATCH 16/38] style changes and fix hidden top row --- scripts/system/html/css/edit-style.css | 58 ++++++++++++++------------ scripts/system/html/entityList.html | 28 ++++++------- scripts/system/html/js/entityList.js | 7 +++- 3 files changed, 52 insertions(+), 41 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 088b0952ae..1b0094cfb7 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -273,7 +273,6 @@ input[type="number"] { height: 28px; width: 124px; } - input[type=number] { padding-right: 3px; } @@ -1085,66 +1084,68 @@ textarea:enabled[scrolling="true"]::-webkit-resizer { bottom: 0; } +#filter-type-selectBox select { + border-radius: 14.5px; +} #filter-type-checkboxes { position: absolute; z-index: 2; - top: 28px; + top: 48px; display: none; border: none; } #filter-type-checkboxes div { position: relative; - height: 25px; + height: 22px; } #filter-type-checkboxes span { position: relative; top: 3px; font-family: hifi-glyphs; - font-size: 16px; - color: #404040; - padding-left: 12px; - padding-right: 12px; + font-size: 13px; + color: #000000; + padding-left: 6px; + padding-right: 4px; } #filter-type-checkboxes label { - position: relative; - top: -13px; - z-index: 3; + position: absolute; + top: -20px; + z-index: 2; display: block; font-family: FiraSans-SemiBold; - color: #404040; + font-size: 11px; + color: #000000; background-color: #afafaf; + width: 200px; + height: 22px; padding-top: 1px; - padding-right: 12px; - height: 24px; } #filter-type-checkboxes label:hover { background-color: #1e90ff; } -#filter-type-checkboxes input[type=checkbox] { - position: relative; - top: 6px; - right: -10px; - z-index: 4; - display: block; -} #filter-type-checkboxes input[type=checkbox] + label { - background-image: none; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAYAAAAehFoBAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH4goSADUOYnF4LQAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAIMSURBVFjD7ZmxkqowFIZ/7mwJPen1AezV3t6hFvrQhweAHvrQ8wL2xt4HwD7ppd+tvHOvu0gCYdEZTsmAfpNzzpcTcAB84o3iD94sZuCx4+Pxwvl8dl4JcL1ef84lMQPPwBZDSgkp5XsASylBKUUYhhBCvDbw7XYDpRRKKTRNA8YYOOevC5ymKZRS/13jnHdCTwLMOW8tAc45GGNomuY1gKuq6lxFIQQopdMDXy4X5HmudW8URdMCSynBGNOG3Ww20wHf9dVWl4+wbav7a8CMsW9G+Cm22+1T2F8BzvMc1+u18z5CCJIkseNhKSX2+z2qqjLWl84zhBAURQHXde0A31Oa57nWbqSrLwDwPA9FUcD3fTtb82NKu8QOAHVda+srSRJt2E7gtpQKIXA4HH6csmzpyxj4dDo9TalSCpRS1HX9TV86RujSlxGwlBJpmnY+rJRCGIZ/s2BTX9qnZgBwHAee52mJ/l7nx+PRqr6MVtj3fZRlaVRf/5aGDX0Z17DrusiyrHfqhuqrt9aiKEIcx4OBTfU1aOMIggBlWYIQ0utP+uhr8CyxXC5RFIUxdBAE1srKePgxbcbVamWlnAZNa7rNSAhBlmWv8yLlWTPa0Nco83BbM2ZZZsUIowzwj80YxzEWi8VoB4IPGz9yb0YhBHa73agnGGtHJNd1R4ed9FVV33Awf6ebgd8b+Av9A/rq6s3hjgAAAABJRU5ErkJggg=='); + background-size: 11px 11px; + background-position: top 5px left 14px; } #filter-type-checkboxes input[type=checkbox]:checked + label { - background-image: none; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAYAAAAehFoBAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH4goSADMveELP9QAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAIqSURBVFjD7ZmxkqowFIb/7GwJPfT6APZib+9QC33o4QGghz70vIC9sfcBsE966bPNWlxnlQTDRWc4JUT4hpPz5SQSAAofFF/4sJiBx47v+wun04m8E+B6vVbzlJiBZ2CLIYRQQgj1EcBCCEUpRRRF4Jyrtwa+Xq+glEJKia7rkKYpGGPqbYHzPFdSyn+uMcZ6oScBZowpzvmje0jTVHVd9x7ATdMoxtjTMZxzUErV5MDn81mVZak1No7jab+wEEKlaaoNGwQBmQz4pq9H8/IeNo5jMmnRpWmKeyP8FZvN5insfwEuy1JdLpfecb7vI8uy3tb2Szelu91ONU1jtP9jjKmmabRgq6qC4zh2VrpbSsuy1FqNdPUFAK7roqoqeJ6ntXH4Mk1pn9gBoG1bbX1lWaYN2wv8KKWcc+z3+z+7LFv6MgY+Ho9PUyqlBKUUbduqe33pGKFPX0bAQgiV53nvj6WUiKIIt2K0qS/tXTMAEELguq6W6H/nOQ6Hg1V9GX1hz/NIXdckCALtB7Vta1VfxnPYcRwURUEeNSGmYaqvwVqL45gkSfIysKm+Xlo4wjAkdV3D9/1BLxmir5d7ieVySaqqMoYOw3CwEV5ufkyLcbVaIUkSq2d1xt2abjH6vo+iKKwfLA5uL58Vow19jdIPPyrGoiisGGGUBv6+GJMkwWKxGO2M+dvGQ36LEZxztd1uRz0Qt7ZFchwHY8NOelQ1NAjm/+lm4M8G/gH2zx33BSr7jAAAAABJRU5ErkJggg=='); + background-size: 11px 11px; + background-position: top 5px left 14px; } #filter-type-checkboxes input[type=checkbox]:hover + label { background-color: #1e90ff; } #filter-search-and-icon { - position: absolute; - left: 120px; - width: calc(100% - 300px); + position: relative; + left: 118px; + width: calc(100% - 126px); } #filter-in-view { position: absolute; + top: 0px; right: 126px; } @@ -1152,13 +1153,18 @@ textarea:enabled[scrolling="true"]::-webkit-resizer { position: relative; float: right; margin-right: -168px; - top: -17px; + top: -45px; } #filter-radius-and-unit label { margin-left: 2px; } #filter-radius-and-unit input { width: 120px; + border-radius: 14.5px; + font-style: italic; +} +#filter-radius-and-unit input[type=number]::-webkit-inner-spin-button { + display: none; } #entity-table-scroll { diff --git a/scripts/system/html/entityList.html b/scripts/system/html/entityList.html index 8371e2cbd7..dc022c9ab9 100644 --- a/scripts/system/html/entityList.html +++ b/scripts/system/html/entityList.html @@ -30,20 +30,20 @@
-
-
- -
-
-
- -
-
-
- Y -
+
+
+ +
+
+
+ +
+
+
+ Y +
diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 400cdc7dce..66ad08e27c 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -23,6 +23,7 @@ const FILTER_IN_VIEW_ATTRIBUTE = "pressed"; const WINDOW_NONVARIABLE_HEIGHT = 227; const NUM_COLUMNS = 12; const EMPTY_ENTITY_ID = "0"; +const MAX_LENGTH_RADIUS = 9; const DELETE = 46; // Key code for the delete key. const KEY_P = 80; // Key code for letter p used for Parenting hotkey. @@ -203,7 +204,11 @@ function loaded() { elFilterSearch.onsearch = refreshEntityList; elFilterInView.onclick = toggleFilterInView; elFilterRadius.onchange = onRadiusChange; - elInfoToggle.onclick = toggleInfo; + elFilterRadius.oninput = function(event) { + if (event.target.value.length > MAX_LENGTH_RADIUS) { + event.target.value = event.target.value.slice(0, MAX_LENGTH_RADIUS); + } + } // create filter type dropdown checkboxes with label and icon for each type elFilterTypeSelectBox.onclick = toggleTypeDropdown; From 9e35af0c4a3ff246d3fe32410c9b67952f22e265 Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 17 Oct 2018 18:24:09 -0700 Subject: [PATCH 17/38] remove space --- scripts/system/html/entityList.html | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/system/html/entityList.html b/scripts/system/html/entityList.html index dc022c9ab9..434f8a5f87 100644 --- a/scripts/system/html/entityList.html +++ b/scripts/system/html/entityList.html @@ -100,7 +100,6 @@ -
There are no entities to display. Please check your filters or create an entity to begin.
From a4adf2c71678853b4edd3737177585b3e1a5a9ab Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 18 Oct 2018 17:39:24 -0700 Subject: [PATCH 18/38] Fix name of Particles to ParticleEffect for default properties in edit.js --- scripts/system/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 96d3d763b0..71b4b2c54d 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -419,7 +419,7 @@ const DEFAULT_ENTITY_PROPERTIES = { sourceUrl: "https://highfidelity.com/", dpi: 30, }, - Particles: { + ParticleEffect: { lifespan: 1.5, maxParticles: 10, textures: "https://content.highfidelity.com/DomainContent/production/Particles/wispy-smoke.png", From bb5c042f16bc0dd86785861af429db54925a572d Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 18 Oct 2018 17:51:20 -0700 Subject: [PATCH 19/38] Blend network animation --- .../resources/avatar/network-animation.json | 140 ++++++++++++++++++ interface/src/avatar/AvatarManager.cpp | 37 +---- interface/src/avatar/MyAvatar.cpp | 17 --- interface/src/avatar/MyAvatar.h | 2 - libraries/animation/src/AnimClip.h | 2 + libraries/animation/src/Rig.cpp | 117 ++++++++------- libraries/animation/src/Rig.h | 23 ++- .../src/avatars-renderer/Avatar.cpp | 24 +-- .../src/avatars-renderer/Avatar.h | 21 --- .../controllers/controllerModules/teleport.js | 2 - 10 files changed, 241 insertions(+), 144 deletions(-) create mode 100644 interface/resources/avatar/network-animation.json diff --git a/interface/resources/avatar/network-animation.json b/interface/resources/avatar/network-animation.json new file mode 100644 index 0000000000..0ba4ea465c --- /dev/null +++ b/interface/resources/avatar/network-animation.json @@ -0,0 +1,140 @@ +{ + "version": "1.1", + "root": { + "id": "userAnimStateMachine", + "type": "stateMachine", + "data": { + "currentState": "idleAnim", + "states": [ + { + "id": "idleAnim", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "postTransitAnim", "state": "postTransitAnim" }, + { "var": "preTransitAnim", "state": "preTransitAnim" } + ] + }, + { + "id": "preTransitAnim", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "idleAnim", "state": "idleAnim" }, + { "var": "transitAnim", "state": "transitAnim" } + ] + }, + { + "id": "transitAnim", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "preTransitAnim", "state": "preTransitAnim" }, + { "var": "postTransitAnim", "state": "postTransitAnim" } + ] + }, + { + "id": "postTransitAnim", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "transitAnim", "state": "transitAnim" }, + { "var": "idleAnim", "state": "idleAnim" } + ] + }, + { + "id": "userAnimA", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "idleAnim", "state": "idleAnim" }, + { "var": "userAnimB", "state": "userAnimB" } + ] + }, + { + "id": "userAnimB", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "idleAnim", "state": "idleAnim" }, + { "var": "userAnimA", "state": "userAnimA" } + ] + } + ] + }, + "children": [ + { + "id": "idleAnim", + "type": "clip", + "data": { + "url": "qrc:///avatar/animations/idle.fbx", + "startFrame": 0.0, + "endFrame": 90.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "preTransitAnim", + "type": "clip", + "data": { + "url": "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx", + "startFrame": 0.0, + "endFrame": 10.0, + "timeScale": 1.0, + "loopFlag": false + }, + "children": [] + }, + { + "id": "transitAnim", + "type": "clip", + "data": { + "url": "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx", + "startFrame": 11.0, + "endFrame": 11.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "postTransitAnim", + "type": "clip", + "data": { + "url": "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx", + "startFrame": 22.0, + "endFrame": 49.0, + "timeScale": 1.0, + "loopFlag": false + }, + "children": [] + }, + { + "id": "userAnimA", + "type": "clip", + "data": { + "url": "qrc:///avatar/animations/idle.fbx", + "startFrame": 0.0, + "endFrame": 90.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "userAnimB", + "type": "clip", + "data": { + "url": "qrc:///avatar/animations/idle.fbx", + "startFrame": 0.0, + "endFrame": 90.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + } + ] + } +} diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index c3f6579e90..dd56c03d26 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -83,18 +83,10 @@ AvatarManager::AvatarManager(QObject* parent) : const int AVATAR_TRANSIT_FRAME_COUNT = 11; // Based on testing const float AVATAR_TRANSIT_FRAMES_PER_METER = 0.5f; // Based on testing - const QString START_ANIMATION_URL = "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx"; - const QString MIDDLE_ANIMATION_URL = "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx"; - const QString END_ANIMATION_URL = "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx"; - _transitConfig._totalFrames = AVATAR_TRANSIT_FRAME_COUNT; _transitConfig._triggerDistance = AVATAR_TRANSIT_TRIGGER_DISTANCE; _transitConfig._framesPerMeter = AVATAR_TRANSIT_FRAMES_PER_METER; _transitConfig._isDistanceBased = true; - - _transitConfig._startTransitAnimation = AvatarTransit::TransitAnimation(START_ANIMATION_URL, 0, 10); - _transitConfig._middleTransitAnimation = AvatarTransit::TransitAnimation(MIDDLE_ANIMATION_URL, 15, 0); - _transitConfig._endTransitAnimation = AvatarTransit::TransitAnimation(END_ANIMATION_URL, 22, 27); } AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { @@ -143,30 +135,22 @@ void AvatarManager::setSpace(workload::SpacePointer& space ) { } void AvatarManager::handleTransitAnimations(AvatarTransit::Status status) { - auto startAnimation = _transitConfig._startTransitAnimation; - auto middleAnimation = _transitConfig._middleTransitAnimation; - auto endAnimation = _transitConfig._endTransitAnimation; - - const float REFERENCE_FPS = 30.0f; switch (status) { case AvatarTransit::Status::STARTED: qDebug() << "START_FRAME"; - _myAvatar->overrideNetworkAnimation(startAnimation._animationUrl, REFERENCE_FPS, false, startAnimation._firstFrame, - startAnimation._firstFrame + startAnimation._frameCount); + _myAvatar->getSkeletonModel()->getRig().triggerNetworkAnimation("preTransitAnim"); break; case AvatarTransit::Status::START_TRANSIT: qDebug() << "START_TRANSIT"; - _myAvatar->overrideNetworkAnimation(middleAnimation._animationUrl, REFERENCE_FPS, false, middleAnimation._firstFrame, - middleAnimation._firstFrame + middleAnimation._frameCount); + _myAvatar->getSkeletonModel()->getRig().triggerNetworkAnimation("transitAnim"); break; case AvatarTransit::Status::END_TRANSIT: qDebug() << "END_TRANSIT"; - _myAvatar->overrideNetworkAnimation(endAnimation._animationUrl, REFERENCE_FPS, false, endAnimation._firstFrame, - endAnimation._firstFrame + endAnimation._frameCount); + _myAvatar->getSkeletonModel()->getRig().triggerNetworkAnimation("postTransitAnim"); break; case AvatarTransit::Status::ENDED: qDebug() << "END_FRAME"; - _myAvatar->restoreNetworkAnimation(); + _myAvatar->getSkeletonModel()->getRig().triggerNetworkAnimation("idleAnim"); break; case AvatarTransit::Status::PRE_TRANSIT: break; @@ -188,9 +172,6 @@ void AvatarManager::updateMyAvatar(float deltaTime) { AvatarTransit::Status status = _myAvatar->updateTransit(deltaTime, _myAvatar->getNextPosition(), _transitConfig); handleTransitAnimations(status); - bool sendFirstTransitPacket = (status == AvatarTransit::Status::STARTED); - bool sendCurrentTransitPacket = (status != AvatarTransit::Status::IDLE && (status != AvatarTransit::Status::POST_TRANSIT || status != AvatarTransit::Status::ENDED)); - _myAvatar->update(deltaTime); render::Transaction transaction; _myAvatar->updateRenderItem(transaction); @@ -199,19 +180,13 @@ void AvatarManager::updateMyAvatar(float deltaTime) { quint64 now = usecTimestampNow(); quint64 dt = now - _lastSendAvatarDataTime; - if (sendFirstTransitPacket || (dt > MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS && !_myAvatarDataPacketsPaused)) { + if (dt > MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS && !_myAvatarDataPacketsPaused) { // send head/hand data to the avatar mixer and voxel server - PerformanceTimer perfTimer("send"); - if (sendFirstTransitPacket) { - _myAvatar->overrideNextPacketPositionData(_myAvatar->getTransit()->getEndPosition()); - } else if (sendCurrentTransitPacket) { - _myAvatar->overrideNextPacketPositionData(_myAvatar->getTransit()->getCurrentPosition()); - } + PerformanceTimer perfTimer("send"); _myAvatar->sendAvatarDataPacket(); _lastSendAvatarDataTime = now; _myAvatarSendRate.increment(); } - } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 152215e717..b347963cf1 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1124,15 +1124,6 @@ void MyAvatar::overrideAnimation(const QString& url, float fps, bool loop, float _skeletonModel->getRig().overrideAnimation(url, fps, loop, firstFrame, lastFrame); } -void MyAvatar::overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "overrideNetworkAnimation", Q_ARG(const QString&, url), Q_ARG(float, fps), - Q_ARG(bool, loop), Q_ARG(float, firstFrame), Q_ARG(float, lastFrame)); - return; - } - _skeletonModel->getRig().overrideNetworkAnimation(url, fps, loop, firstFrame, lastFrame); -} - void MyAvatar::restoreAnimation() { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "restoreAnimation"); @@ -1141,14 +1132,6 @@ void MyAvatar::restoreAnimation() { _skeletonModel->getRig().restoreAnimation(); } -void MyAvatar::restoreNetworkAnimation() { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "restoreNetworkAnimation"); - return; - } - _skeletonModel->getRig().restoreNetworkAnimation(); -} - QStringList MyAvatar::getAnimationRoles() { if (QThread::currentThread() != thread()) { QStringList result; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 9770a5bb1a..16b765711a 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -376,7 +376,6 @@ public: * }, 3000); */ Q_INVOKABLE void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); - Q_INVOKABLE void overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); /**jsdoc * The avatar animation system includes a set of default animations along with rules for how those animations are blended together with @@ -393,7 +392,6 @@ public: * }, 3000); */ Q_INVOKABLE void restoreAnimation(); - Q_INVOKABLE void restoreNetworkAnimation(); /**jsdoc * Each avatar has an avatar-animation.json file that defines which animations are used and how they are blended together with procedural data diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index eba361fd4c..92f4c01dcc 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -51,6 +51,8 @@ public: bool getMirrorFlag() const { return _mirrorFlag; } void setMirrorFlag(bool mirrorFlag) { _mirrorFlag = mirrorFlag; } + float getFrame() const { return _frame; } + void loadURL(const QString& url); protected: diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index b9e654964a..df7e322da7 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -31,6 +31,7 @@ #include "AnimSkeleton.h" #include "AnimUtil.h" #include "IKTarget.h" +#include "PathUtils.h" static int nextRigId = 1; @@ -133,41 +134,28 @@ void Rig::overrideAnimation(const QString& url, float fps, bool loop, float firs _animVars.set("userAnimB", clipNodeEnum == UserAnimState::B); } -void Rig::overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { - UserAnimState::ClipNodeEnum clipNodeEnum; - if (_networkAnimState.clipNodeEnum == UserAnimState::None || _networkAnimState.clipNodeEnum == UserAnimState::B) { - clipNodeEnum = UserAnimState::A; +void Rig::triggerNetworkAnimation(const QString& animName) { + _networkVars.set("idleAnim", false); + _networkVars.set("preTransitAnim", false); + _networkVars.set("transitAnim", false); + _networkVars.set("postTransitAnim", false); + _sendNetworkNode = true; + + if (animName == "idleAnim") { + _networkVars.set("idleAnim", true); + _networkAnimState.clipNodeEnum = NetworkAnimState::Idle; + _sendNetworkNode = false; + } else if (animName == "preTransitAnim") { + _networkVars.set("preTransitAnim", true); + _networkAnimState.clipNodeEnum = NetworkAnimState::PreTransit; + } else if (animName == "transitAnim") { + _networkVars.set("transitAnim", true); + _networkAnimState.clipNodeEnum = NetworkAnimState::Transit; + } else if (animName == "postTransitAnim") { + _networkVars.set("postTransitAnim", true); + _networkAnimState.clipNodeEnum = NetworkAnimState::PostTransit; } - else { - clipNodeEnum = UserAnimState::B; - } - if (_networkNode) { - // find an unused AnimClip clipNode - _sendNetworkNode = true; - std::shared_ptr clip; - if (clipNodeEnum == UserAnimState::A) { - clip = std::dynamic_pointer_cast(_networkNode->findByName("userAnimA")); - } - else { - clip = std::dynamic_pointer_cast(_networkNode->findByName("userAnimB")); - } - if (clip) { - // set parameters - clip->setLoopFlag(loop); - clip->setStartFrame(firstFrame); - clip->setEndFrame(lastFrame); - const float REFERENCE_FRAMES_PER_SECOND = 30.0f; - float timeScale = fps / REFERENCE_FRAMES_PER_SECOND; - clip->setTimeScale(timeScale); - clip->loadURL(url); - } - } - // store current user anim state. - _networkAnimState = { clipNodeEnum, url, fps, loop, firstFrame, lastFrame }; - // notify the userAnimStateMachine the desired state. - _networkVars.set("userAnimNone", false); - _networkVars.set("userAnimA", clipNodeEnum == UserAnimState::A); - _networkVars.set("userAnimB", clipNodeEnum == UserAnimState::B); + } void Rig::restoreAnimation() { @@ -182,13 +170,12 @@ void Rig::restoreAnimation() { } void Rig::restoreNetworkAnimation() { - _sendNetworkNode = false; - if (_networkAnimState.clipNodeEnum != UserAnimState::None) { - _networkAnimState.clipNodeEnum = UserAnimState::None; - // notify the userAnimStateMachine the desired state. - _networkVars.set("userAnimNone", true); - _networkVars.set("userAnimA", false); - _networkVars.set("userAnimB", false); + if (_networkAnimState.clipNodeEnum != NetworkAnimState::Idle) { + _networkAnimState.clipNodeEnum = NetworkAnimState::Idle; + _networkVars.set("idleAnim", true); + _networkVars.set("preTransitAnim", false); + _networkVars.set("transitAnim", false); + _networkVars.set("postTransitAnim", false); } } @@ -1137,6 +1124,24 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons _internalPoseSet._relativePoses = _animNode->evaluate(_animVars, context, deltaTime, triggersOut); if (_networkNode) { _networkPoseSet._relativePoses = _networkNode->evaluate(_networkVars, context, deltaTime, networkTriggersOut); + float alpha = 1.0f; + std::shared_ptr clip; + if (_networkAnimState.clipNodeEnum == NetworkAnimState::PreTransit) { + clip = std::dynamic_pointer_cast(_networkNode->findByName("preTransitAnim")); + if (clip) { + alpha = (clip->getFrame() - clip->getStartFrame()) / 6.0f; + } + } else if (_networkAnimState.clipNodeEnum == NetworkAnimState::PostTransit) { + clip = std::dynamic_pointer_cast(_networkNode->findByName("postTransitAnim")); + if (clip) { + alpha = (clip->getEndFrame() - clip->getFrame()) / 6.0f; + } + } + if (_sendNetworkNode) { + for (int i = 0; i < _networkPoseSet._relativePoses.size(); i++) { + _networkPoseSet._relativePoses[i].blend(_internalPoseSet._relativePoses[i], (alpha > 1.0f ? 1.0f : alpha)); + } + } } if ((int)_internalPoseSet._relativePoses.size() != _animSkeleton->getNumJoints()) { // animations haven't fully loaded yet. @@ -1798,10 +1803,10 @@ void Rig::initAnimGraph(const QUrl& url) { // load the anim graph _animLoader.reset(new AnimNodeLoader(url)); - _networkLoader.reset(new AnimNodeLoader(url)); - + auto networkUrl = PathUtils::resourcesUrl("avatar/network-animation.json"); + _networkLoader.reset(new AnimNodeLoader(networkUrl)); std::weak_ptr weakSkeletonPtr = _animSkeleton; - connect(_animLoader.get(), &AnimNodeLoader::success, [this, weakSkeletonPtr](AnimNode::Pointer nodeIn) { + connect(_animLoader.get(), &AnimNodeLoader::success, [this, weakSkeletonPtr, url](AnimNode::Pointer nodeIn) { _animNode = nodeIn; // abort load if the previous skeleton was deleted. @@ -1828,10 +1833,10 @@ void Rig::initAnimGraph(const QUrl& url) { emit onLoadComplete(); }); connect(_animLoader.get(), &AnimNodeLoader::error, [url](int error, QString str) { - qCCritical(animation) << "Error loading" << url.toDisplayString() << "code = " << error << "str =" << str; + qCritical(animation) << "Error loading" << url.toDisplayString() << "code = " << error << "str =" << str; }); - connect(_networkLoader.get(), &AnimNodeLoader::success, [this, weakSkeletonPtr](AnimNode::Pointer nodeIn) { + connect(_networkLoader.get(), &AnimNodeLoader::success, [this, weakSkeletonPtr, networkUrl](AnimNode::Pointer nodeIn) { _networkNode = nodeIn; // abort load if the previous skeleton was deleted. auto sharedSkeletonPtr = weakSkeletonPtr.lock(); @@ -1839,16 +1844,22 @@ void Rig::initAnimGraph(const QUrl& url) { return; } _networkNode->setSkeleton(sharedSkeletonPtr); - if (_networkAnimState.clipNodeEnum != UserAnimState::None) { + if (_networkAnimState.clipNodeEnum != NetworkAnimState::Idle) { // restore the user animation we had before reset. - UserAnimState origState = _networkAnimState; - _networkAnimState = { UserAnimState::None, "", 30.0f, false, 0.0f, 0.0f }; - overrideNetworkAnimation(origState.url, origState.fps, origState.loop, origState.firstFrame, origState.lastFrame); + NetworkAnimState origState = _networkAnimState; + _networkAnimState = { NetworkAnimState::Idle, "", 30.0f, false, 0.0f, 0.0f }; + if (_networkAnimState.clipNodeEnum == NetworkAnimState::PreTransit) { + triggerNetworkAnimation("preTransitAnim"); + } else if (_networkAnimState.clipNodeEnum == NetworkAnimState::Transit) { + triggerNetworkAnimation("transitAnim"); + } else if (_networkAnimState.clipNodeEnum == NetworkAnimState::PostTransit) { + triggerNetworkAnimation("postTransitAnim"); + } } - // emit onLoadComplete(); + }); - connect(_networkLoader.get(), &AnimNodeLoader::error, [url](int error, QString str) { - qCCritical(animation) << "Error loading" << url.toDisplayString() << "code = " << error << "str =" << str; + connect(_networkLoader.get(), &AnimNodeLoader::error, [networkUrl](int error, QString str) { + qCritical(animation) << "Error loading" << networkUrl.toDisplayString() << "code = " << error << "str =" << str; }); } } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 37d1ec1dd3..af786a5e70 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -113,7 +113,7 @@ public: void destroyAnimGraph(); void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); - void overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); + void triggerNetworkAnimation(const QString& animName); void restoreAnimation(); void restoreNetworkAnimation(); @@ -323,6 +323,25 @@ protected: RigRole _state { RigRole::Idle }; RigRole _desiredState { RigRole::Idle }; float _desiredStateAge { 0.0f }; + + struct NetworkAnimState { + enum ClipNodeEnum { + Idle = 0, + PreTransit, + Transit, + PostTransit + }; + NetworkAnimState() : clipNodeEnum(NetworkAnimState::Idle) {} + NetworkAnimState(ClipNodeEnum clipNodeEnumIn, const QString& urlIn, float fpsIn, bool loopIn, float firstFrameIn, float lastFrameIn) : + clipNodeEnum(clipNodeEnumIn), url(urlIn), fps(fpsIn), loop(loopIn), firstFrame(firstFrameIn), lastFrame(lastFrameIn) {} + + ClipNodeEnum clipNodeEnum; + QString url; + float fps; + bool loop; + float firstFrame; + float lastFrame; + }; struct UserAnimState { enum ClipNodeEnum { @@ -357,7 +376,7 @@ protected: }; UserAnimState _userAnimState; - UserAnimState _networkAnimState; + NetworkAnimState _networkAnimState; std::map _roleAnimStates; float _leftHandOverlayAlpha { 0.0f }; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index b43e1c23f6..705ebc0b13 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -114,12 +114,10 @@ void Avatar::setShowNamesAboveHeads(bool show) { } AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config) { - bool checkDistance = (!_isActive || (_status == Status::POST_TRANSIT || _status == Status::ENDED)); float oneFrameDistance = _isActive ? glm::length(avatarPosition - _endPosition) : glm::length(avatarPosition - _lastPosition); - const float TRANSIT_TRIGGER_MAX_DISTANCE = 30.0f; float scaledMaxTransitDistance = TRANSIT_TRIGGER_MAX_DISTANCE * _scale; - if (oneFrameDistance > config._triggerDistance && checkDistance) { + if (oneFrameDistance > config._triggerDistance) { if (oneFrameDistance < scaledMaxTransitDistance) { start(deltaTime, _lastPosition, avatarPosition, config); } else { @@ -132,12 +130,9 @@ AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& av const float SETTLE_ABORT_DISTANCE = 0.1f; float scaledSettleAbortDistance = SETTLE_ABORT_DISTANCE * _scale; - bool abortPostTransit = (_status == Status::POST_TRANSIT && _purpose == Purpose::SIT) || - (_isActive && oneFrameDistance > scaledSettleAbortDistance && _status == Status::POST_TRANSIT); - if (abortPostTransit) { + if (_isActive && oneFrameDistance > scaledSettleAbortDistance && _status == Status::POST_TRANSIT) { reset(); _status = Status::ENDED; - _purpose = Purpose::UNDEFINED; } return _status; } @@ -154,10 +149,13 @@ void AvatarTransit::start(float deltaTime, const glm::vec3& startPosition, const _transitLine = endPosition - startPosition; _totalDistance = glm::length(_transitLine); _easeType = config._easeType; - const float REFERENCE_FRAMES_PER_SECOND = 30.0f; - _preTransitTime = (float)config._startTransitAnimation._frameCount / REFERENCE_FRAMES_PER_SECOND; - _postTransitTime = (float)config._endTransitAnimation._frameCount / REFERENCE_FRAMES_PER_SECOND; + const float REFERENCE_FRAMES_PER_SECOND = 30.0f; + const float PRE_TRANSIT_FRAME_COUNT = 10.0f; + const float POST_TRANSIT_FRAME_COUNT = 27.0f; + + _preTransitTime = PRE_TRANSIT_FRAME_COUNT / REFERENCE_FRAMES_PER_SECOND; + _postTransitTime = POST_TRANSIT_FRAME_COUNT / REFERENCE_FRAMES_PER_SECOND; int transitFrames = (!config._isDistanceBased) ? config._totalFrames : config._framesPerMeter * _totalDistance; _transitTime = (float)transitFrames / REFERENCE_FRAMES_PER_SECOND; _totalTime = _transitTime + _preTransitTime + _postTransitTime; @@ -206,7 +204,6 @@ AvatarTransit::Status AvatarTransit::updatePosition(float deltaTime) { _currentPosition = _endPosition; if (nextTime >= _totalTime) { _isActive = false; - _purpose = Purpose::UNDEFINED; status = Status::ENDED; } else if (_currentTime < _totalTime - _postTransitTime) { status = Status::END_TRANSIT; @@ -2007,11 +2004,6 @@ void Avatar::setTransitScale(float scale) { _transit.setScale(scale); } -void Avatar::setTransitPurpose(int purpose) { - std::lock_guard lock(_transitLock); - _transit.setPurpose(static_cast(purpose)); -} - void Avatar::overrideNextPacketPositionData(const glm::vec3& position) { std::lock_guard lock(_transitLock); _overrideGlobalPosition = true; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index d375909609..2995c0f7b4 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -71,21 +71,6 @@ public: EASE_IN_OUT }; - enum Purpose { - UNDEFINED = 0, - TELEPORT, - SIT - }; - - struct TransitAnimation { - TransitAnimation(){}; - TransitAnimation(const QString& animationUrl, int firstFrame, int frameCount) : - _firstFrame(firstFrame), _frameCount(frameCount), _animationUrl(animationUrl){}; - int _firstFrame; - int _frameCount; - QString _animationUrl; - }; - struct TransitConfig { TransitConfig() {}; int _totalFrames { 0 }; @@ -93,9 +78,6 @@ public: bool _isDistanceBased { false }; float _triggerDistance { 0 }; EaseType _easeType { EaseType::EASE_OUT }; - TransitAnimation _startTransitAnimation; - TransitAnimation _middleTransitAnimation; - TransitAnimation _endTransitAnimation; }; AvatarTransit() {}; @@ -105,7 +87,6 @@ public: glm::vec3 getCurrentPosition() { return _currentPosition; } glm::vec3 getEndPosition() { return _endPosition; } void setScale(float scale) { _scale = scale; } - void setPurpose(const Purpose& purpose) { _purpose = purpose; } void reset(); private: @@ -130,7 +111,6 @@ private: EaseType _easeType { EaseType::EASE_OUT }; Status _status { Status::IDLE }; float _scale { 1.0f }; - Purpose _purpose { Purpose::UNDEFINED }; }; class Avatar : public AvatarData, public scriptable::ModelProvider { @@ -457,7 +437,6 @@ public: AvatarTransit::Status updateTransit(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config); void setTransitScale(float scale); - Q_INVOKABLE void setTransitPurpose(int purpose); void overrideNextPacketPositionData(const glm::vec3& position); diff --git a/scripts/system/controllers/controllerModules/teleport.js b/scripts/system/controllers/controllerModules/teleport.js index d31f207b19..bf5022cdaf 100644 --- a/scripts/system/controllers/controllerModules/teleport.js +++ b/scripts/system/controllers/controllerModules/teleport.js @@ -781,13 +781,11 @@ Script.include("/~/system/libraries/controllers.js"); if (target === TARGET.NONE || target === TARGET.INVALID) { // Do nothing } else if (target === TARGET.SEAT) { - MyAvatar.setTransitPurpose(2); Entities.callEntityMethod(result.objectID, 'sit'); } else if (target === TARGET.SURFACE || target === TARGET.DISCREPANCY) { var offset = getAvatarFootOffset(); result.intersection.y += offset; var shouldLandSafe = target === TARGET.DISCREPANCY; - MyAvatar.setTransitPurpose(1); MyAvatar.goToLocation(result.intersection, true, HMD.orientation, false, shouldLandSafe); HMD.centerUI(); MyAvatar.centerBody(); From 31d099907aea02a6a11ca357b86ef80d3896413a Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Fri, 19 Oct 2018 14:00:52 -0700 Subject: [PATCH 20/38] Fix warnings --- libraries/animation/src/Rig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index df7e322da7..35f076c78d 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1138,7 +1138,7 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons } } if (_sendNetworkNode) { - for (int i = 0; i < _networkPoseSet._relativePoses.size(); i++) { + for (auto i = 0; i < _networkPoseSet._relativePoses.size(); i++) { _networkPoseSet._relativePoses[i].blend(_internalPoseSet._relativePoses[i], (alpha > 1.0f ? 1.0f : alpha)); } } From f8d67d124fc8b80623f4557eeffeb944314f1de6 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Sat, 20 Oct 2018 15:22:25 -0700 Subject: [PATCH 21/38] Hold flickering fixed and refactor --- interface/src/avatar/AvatarManager.cpp | 14 +++--- interface/src/avatar/MyAvatar.cpp | 1 - libraries/animation/src/Rig.cpp | 2 +- .../src/avatars-renderer/Avatar.cpp | 45 +++++-------------- .../src/avatars-renderer/Avatar.h | 10 ++--- libraries/avatars/src/AvatarData.cpp | 29 +++++------- libraries/avatars/src/AvatarData.h | 15 +++++-- 7 files changed, 46 insertions(+), 70 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index dd56c03d26..bd3fba9a69 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -79,14 +79,12 @@ AvatarManager::AvatarManager(QObject* parent) : } }); - const float AVATAR_TRANSIT_TRIGGER_DISTANCE = 1.0f; - const int AVATAR_TRANSIT_FRAME_COUNT = 11; // Based on testing - const float AVATAR_TRANSIT_FRAMES_PER_METER = 0.5f; // Based on testing - _transitConfig._totalFrames = AVATAR_TRANSIT_FRAME_COUNT; - _transitConfig._triggerDistance = AVATAR_TRANSIT_TRIGGER_DISTANCE; + _transitConfig._minTriggerDistance = AVATAR_TRANSIT_MIN_TRIGGER_DISTANCE; + _transitConfig._maxTriggerDistance = AVATAR_TRANSIT_MAX_TRIGGER_DISTANCE; _transitConfig._framesPerMeter = AVATAR_TRANSIT_FRAMES_PER_METER; - _transitConfig._isDistanceBased = true; + _transitConfig._isDistanceBased = AVATAR_TRANSIT_DISTANCE_BASED; + _transitConfig._abortDistance = AVATAR_TRANSIT_ABORT_DISTANCE; } AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { @@ -169,7 +167,7 @@ void AvatarManager::updateMyAvatar(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "AvatarManager::updateMyAvatar()"); - AvatarTransit::Status status = _myAvatar->updateTransit(deltaTime, _myAvatar->getNextPosition(), _transitConfig); + AvatarTransit::Status status = _myAvatar->updateTransit(deltaTime, _myAvatar->getNextPosition(), _myAvatar->getSensorToWorldScale(), _transitConfig); handleTransitAnimations(status); _myAvatar->update(deltaTime); @@ -300,7 +298,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { if (inView && avatar->hasNewJointData()) { numAvatarsUpdated++; } - auto transitStatus = avatar->_transit.update(deltaTime, avatar->_lastPosition, _transitConfig); + auto transitStatus = avatar->_transit.update(deltaTime, avatar->_serverPosition, _transitConfig); if (avatar->getIsNewAvatar() && (transitStatus == AvatarTransit::Status::START_TRANSIT || transitStatus == AvatarTransit::Status::ABORT_TRANSIT)) { avatar->_transit.reset(); avatar->setIsNewAvatar(false); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b347963cf1..ca283616a5 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -929,7 +929,6 @@ void MyAvatar::updateSensorToWorldMatrix() { updateJointFromController(controller::Action::RIGHT_HAND, _controllerRightHandMatrixCache); if (hasSensorToWorldScaleChanged) { - setTransitScale(sensorToWorldScale); emit sensorToWorldScaleChanged(sensorToWorldScale); } diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 35f076c78d..31c90a3070 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1138,7 +1138,7 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons } } if (_sendNetworkNode) { - for (auto i = 0; i < _networkPoseSet._relativePoses.size(); i++) { + for (size_t i = 0; i < _networkPoseSet._relativePoses.size(); i++) { _networkPoseSet._relativePoses[i].blend(_internalPoseSet._relativePoses[i], (alpha > 1.0f ? 1.0f : alpha)); } } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 705ebc0b13..c140f1bede 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -115,10 +115,8 @@ void Avatar::setShowNamesAboveHeads(bool show) { AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config) { float oneFrameDistance = _isActive ? glm::length(avatarPosition - _endPosition) : glm::length(avatarPosition - _lastPosition); - const float TRANSIT_TRIGGER_MAX_DISTANCE = 30.0f; - float scaledMaxTransitDistance = TRANSIT_TRIGGER_MAX_DISTANCE * _scale; - if (oneFrameDistance > config._triggerDistance) { - if (oneFrameDistance < scaledMaxTransitDistance) { + if (oneFrameDistance > (config._minTriggerDistance * _scale)) { + if (oneFrameDistance < (config._maxTriggerDistance * _scale)) { start(deltaTime, _lastPosition, avatarPosition, config); } else { _lastPosition = avatarPosition; @@ -128,9 +126,7 @@ AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& av _lastPosition = avatarPosition; _status = updatePosition(deltaTime); - const float SETTLE_ABORT_DISTANCE = 0.1f; - float scaledSettleAbortDistance = SETTLE_ABORT_DISTANCE * _scale; - if (_isActive && oneFrameDistance > scaledSettleAbortDistance && _status == Status::POST_TRANSIT) { + if (_isActive && oneFrameDistance > (config._abortDistance * _scale) && _status == Status::POST_TRANSIT) { reset(); _status = Status::ENDED; } @@ -150,14 +146,10 @@ void AvatarTransit::start(float deltaTime, const glm::vec3& startPosition, const _totalDistance = glm::length(_transitLine); _easeType = config._easeType; - const float REFERENCE_FRAMES_PER_SECOND = 30.0f; - const float PRE_TRANSIT_FRAME_COUNT = 10.0f; - const float POST_TRANSIT_FRAME_COUNT = 27.0f; - - _preTransitTime = PRE_TRANSIT_FRAME_COUNT / REFERENCE_FRAMES_PER_SECOND; - _postTransitTime = POST_TRANSIT_FRAME_COUNT / REFERENCE_FRAMES_PER_SECOND; + _preTransitTime = AVATAR_PRE_TRANSIT_FRAME_COUNT / AVATAR_TRANSIT_FRAMES_PER_SECOND; + _postTransitTime = AVATAR_POST_TRANSIT_FRAME_COUNT / AVATAR_TRANSIT_FRAMES_PER_SECOND; int transitFrames = (!config._isDistanceBased) ? config._totalFrames : config._framesPerMeter * _totalDistance; - _transitTime = (float)transitFrames / REFERENCE_FRAMES_PER_SECOND; + _transitTime = (float)transitFrames / AVATAR_TRANSIT_FRAMES_PER_SECOND; _totalTime = _transitTime + _preTransitTime + _postTransitTime; _currentTime = _isActive ? _preTransitTime : 0.0f; _isActive = true; @@ -554,13 +546,10 @@ void Avatar::relayJointDataToChildren() { void Avatar::simulate(float deltaTime, bool inView) { PROFILE_RANGE(simulation, "simulate"); - - if (_transit.isActive()) { - _globalPosition = _transit.getCurrentPosition(); - _globalPositionChanged = usecTimestampNow(); - if (!hasParent()) { - setLocalPosition(_transit.getCurrentPosition()); - } + + _globalPosition = _transit.isActive() ? _transit.getCurrentPosition() : _serverPosition; + if (!hasParent()) { + setLocalPosition(_globalPosition); } _simulationRate.increment(); @@ -1994,22 +1983,12 @@ float Avatar::getUnscaledEyeHeightFromSkeleton() const { } } -AvatarTransit::Status Avatar::updateTransit(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config) { +AvatarTransit::Status Avatar::updateTransit(float deltaTime, const glm::vec3& avatarPosition, float avatarScale, const AvatarTransit::TransitConfig& config) { std::lock_guard lock(_transitLock); + _transit.setScale(avatarScale); return _transit.update(deltaTime, avatarPosition, config); } -void Avatar::setTransitScale(float scale) { - std::lock_guard lock(_transitLock); - _transit.setScale(scale); -} - -void Avatar::overrideNextPacketPositionData(const glm::vec3& position) { - std::lock_guard lock(_transitLock); - _overrideGlobalPosition = true; - _globalPositionOverride = position; -} - void Avatar::addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) { std::lock_guard lock(_materialsLock); _materials[parentMaterialName].push(material); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 2995c0f7b4..3df923b048 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -76,7 +76,9 @@ public: int _totalFrames { 0 }; float _framesPerMeter { 0.0f }; bool _isDistanceBased { false }; - float _triggerDistance { 0 }; + float _minTriggerDistance { 0.0f }; + float _maxTriggerDistance { 0.0f }; + float _abortDistance{ 0.0f }; EaseType _easeType { EaseType::EASE_OUT }; }; @@ -434,11 +436,7 @@ public: virtual scriptable::ScriptableModelBase getScriptableModel() override; std::shared_ptr getTransit() { return std::make_shared(_transit); }; - - AvatarTransit::Status updateTransit(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config); - void setTransitScale(float scale); - - void overrideNextPacketPositionData(const glm::vec3& position); + AvatarTransit::Status updateTransit(float deltaTime, const glm::vec3& avatarPosition, float avatarScale, const AvatarTransit::TransitConfig& config); signals: void targetScaleChanged(float targetScale); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index a22cc4a1d3..4ff3c0192d 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -371,12 +371,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent if (hasAvatarGlobalPosition) { auto startSection = destinationBuffer; - if (_overrideGlobalPosition) { - AVATAR_MEMCPY(_globalPositionOverride); - } else { - AVATAR_MEMCPY(_globalPosition); - } - + AVATAR_MEMCPY(_globalPosition); int numBytes = destinationBuffer - startSection; @@ -894,20 +889,22 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { offset = glm::vec3(row * SPACE_BETWEEN_AVATARS, 0.0f, col * SPACE_BETWEEN_AVATARS); } - auto newValue = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]) + offset; - if (_globalPosition != newValue) { - _lastPosition = _globalPosition = newValue; + _serverPosition = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]) + offset; + auto oneStepDistance = glm::length(_globalPosition - _serverPosition); + if (oneStepDistance <= AVATAR_TRANSIT_MIN_TRIGGER_DISTANCE || oneStepDistance >= AVATAR_TRANSIT_MAX_TRIGGER_DISTANCE) { + _globalPosition = _serverPosition; + // if we don't have a parent, make sure to also set our local position + if (!hasParent()) { + setLocalPosition(_serverPosition); + } + } + if (_globalPosition != _serverPosition) { _globalPositionChanged = now; } sourceBuffer += sizeof(AvatarDataPacket::AvatarGlobalPosition); int numBytesRead = sourceBuffer - startSection; _globalPositionRate.increment(numBytesRead); _globalPositionUpdateRate.increment(); - - // if we don't have a parent, make sure to also set our local position - if (!hasParent()) { - setLocalPosition(newValue); - } } if (hasAvatarBoundingBox) { @@ -2110,10 +2107,6 @@ void AvatarData::sendAvatarDataPacket(bool sendAll) { } } - if (_overrideGlobalPosition) { - _overrideGlobalPosition = false; - } - doneEncoding(cullSmallData); static AvatarDataSequenceNumber sequenceNumber = 0; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index dcdaa70ad7..fcbe05ca52 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -327,6 +327,17 @@ const float AVATAR_DISTANCE_LEVEL_5 = 200.0f; // meters // This is the start location in the Sandbox (xyz: 6270, 211, 6000). const glm::vec3 START_LOCATION(6270, 211, 6000); +// Avatar Transit Constants +const float AVATAR_TRANSIT_MIN_TRIGGER_DISTANCE = 1.0f; +const float AVATAR_TRANSIT_MAX_TRIGGER_DISTANCE = 30.0f; +const int AVATAR_TRANSIT_FRAME_COUNT = 11; +const float AVATAR_TRANSIT_FRAMES_PER_METER = 0.5f; +const float AVATAR_TRANSIT_ABORT_DISTANCE = 0.1f; +const bool AVATAR_TRANSIT_DISTANCE_BASED = true; +const float AVATAR_TRANSIT_FRAMES_PER_SECOND = 30.0f; +const float AVATAR_PRE_TRANSIT_FRAME_COUNT = 10.0f; +const float AVATAR_POST_TRANSIT_FRAME_COUNT = 27.0f; + enum KeyState { NO_KEY_DOWN = 0, INSERT_KEY_DOWN, @@ -1378,9 +1389,7 @@ protected: // where Entities are located. This is currently only used by the mixer to decide how often to send // updates about one avatar to another. glm::vec3 _globalPosition { 0, 0, 0 }; - glm::vec3 _lastPosition { 0, 0, 0 }; - glm::vec3 _globalPositionOverride { 0, 0, 0 }; - bool _overrideGlobalPosition { false }; + glm::vec3 _serverPosition { 0, 0, 0 }; quint64 _globalPositionChanged { 0 }; quint64 _avatarBoundingBoxChanged { 0 }; From ddbe02dd4e9b5993027a86ab72e9a405d80c5056 Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 22 Oct 2018 11:56:47 -0700 Subject: [PATCH 22/38] CR changes and fix extra info toggle --- scripts/system/html/css/edit-style.css | 15 +++++--- scripts/system/html/entityList.html | 6 ++-- scripts/system/html/js/entityList.js | 48 +++++++++++++------------- 3 files changed, 38 insertions(+), 31 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 1b0094cfb7..13952c5e38 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -1063,10 +1063,10 @@ textarea:enabled[scrolling="true"]::-webkit-resizer { .multiselect { position: relative; } -.selectBox { +.select-box { position: absolute; } -.selectBox select { +.select-box select { font-family: FiraSans-SemiBold; font-size: 15px; color: #afafaf; @@ -1076,7 +1076,7 @@ textarea:enabled[scrolling="true"]::-webkit-resizer { width: 107px; text-align-last: center; } -.overSelect { +.over-select { position: absolute; left: 0; right: 0; @@ -1084,7 +1084,7 @@ textarea:enabled[scrolling="true"]::-webkit-resizer { bottom: 0; } -#filter-type-selectBox select { +#filter-type-select-box select { border-radius: 14.5px; } #filter-type-checkboxes { @@ -1158,6 +1158,13 @@ textarea:enabled[scrolling="true"]::-webkit-resizer { #filter-radius-and-unit label { margin-left: 2px; } +#filter-radius-and-unit span { + position: relative; + top: 25px; + right: 9px; + z-index: 2; + font-style: italic; +} #filter-radius-and-unit input { width: 120px; border-radius: 14.5px; diff --git a/scripts/system/html/entityList.html b/scripts/system/html/entityList.html index 434f8a5f87..187aa70347 100644 --- a/scripts/system/html/entityList.html +++ b/scripts/system/html/entityList.html @@ -31,11 +31,11 @@
-
+
-
+
@@ -47,7 +47,7 @@
- +
diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 66ad08e27c..2d248e48e6 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -131,7 +131,7 @@ function loaded() { elToggleLocked = document.getElementById("locked"); elToggleVisible = document.getElementById("visible"); elDelete = document.getElementById("delete"); - elFilterTypeSelectBox = document.getElementById("filter-type-selectBox"); + elFilterTypeSelectBox = document.getElementById("filter-type-select-box"); elFilterTypeText = document.getElementById("filter-type-text"); elFilterTypeCheckboxes = document.getElementById("filter-type-checkboxes"); elFilterSearch = document.getElementById("filter-search"); @@ -155,31 +155,31 @@ function loaded() { document.getElementById("entity-url").onclick = function() { setSortColumn('url'); }; - document.getElementById("entity-locked").onclick = function () { + document.getElementById("entity-locked").onclick = function() { setSortColumn('locked'); }; - document.getElementById("entity-visible").onclick = function () { + document.getElementById("entity-visible").onclick = function() { setSortColumn('visible'); }; - document.getElementById("entity-verticesCount").onclick = function () { + document.getElementById("entity-verticesCount").onclick = function() { setSortColumn('verticesCount'); }; - document.getElementById("entity-texturesCount").onclick = function () { + document.getElementById("entity-texturesCount").onclick = function() { setSortColumn('texturesCount'); }; - document.getElementById("entity-texturesSize").onclick = function () { + document.getElementById("entity-texturesSize").onclick = function() { setSortColumn('texturesSize'); }; - document.getElementById("entity-hasTransparent").onclick = function () { + document.getElementById("entity-hasTransparent").onclick = function() { setSortColumn('hasTransparent'); }; - document.getElementById("entity-isBaked").onclick = function () { + document.getElementById("entity-isBaked").onclick = function() { setSortColumn('isBaked'); }; - document.getElementById("entity-drawCalls").onclick = function () { + document.getElementById("entity-drawCalls").onclick = function() { setSortColumn('drawCalls'); }; - document.getElementById("entity-hasScript").onclick = function () { + document.getElementById("entity-hasScript").onclick = function() { setSortColumn('hasScript'); }; elRefresh.onclick = function() { @@ -203,12 +203,9 @@ function loaded() { elFilterSearch.onkeyup = refreshEntityList; elFilterSearch.onsearch = refreshEntityList; elFilterInView.onclick = toggleFilterInView; + elFilterRadius.onkeyup = onRadiusChange; elFilterRadius.onchange = onRadiusChange; - elFilterRadius.oninput = function(event) { - if (event.target.value.length > MAX_LENGTH_RADIUS) { - event.target.value = event.target.value.slice(0, MAX_LENGTH_RADIUS); - } - } + elInfoToggle.onclick = toggleInfo; // create filter type dropdown checkboxes with label and icon for each type elFilterTypeSelectBox.onclick = toggleTypeDropdown; @@ -225,9 +222,10 @@ function loaded() { let elInput = document.createElement('input'); elInput.setAttribute("type", "checkbox"); elInput.setAttribute("id", typeFilterID); + elInput.setAttribute("filterType", type); elInput.checked = true; // all types are checked initially - toggleTypeFilter(type, false); // add all types to the initial types filter - elInput.onclick = onToggleTypeFilter(type); + toggleTypeFilter(elInput, false); // add all types to the initial types filter + elInput.onclick = onToggleTypeFilter; elDiv.appendChild(elInput); elLabel.insertBefore(elSpan, elLabel.childNodes[0]); elDiv.appendChild(elLabel); @@ -642,6 +640,7 @@ function loaded() { } function onRadiusChange() { + elFilterRadius.value = elFilterRadius.value.replace(/[^0-9]/g, ''); elFilterRadius.value = Math.max(elFilterRadius.value, 0); EventBridge.emitWebEvent(JSON.stringify({ type: 'radius', radius: elFilterRadius.value })); refreshEntities(); @@ -655,11 +654,14 @@ function loaded() { elFilterTypeCheckboxes.style.display = isTypeDropdownVisible() ? "none" : "block"; } - function toggleTypeFilter(type, refresh) { + function toggleTypeFilter(elInput, refresh) { + let type = elInput.getAttribute("filterType"); + let typeChecked = elInput.checked; + let typeFilterIndex = typeFilters.indexOf(type); - if (typeFilterIndex > -1) { + if (!typeChecked && typeFilterIndex > -1) { typeFilters.splice(typeFilterIndex, 1); - } else { + } else if (typeChecked && typeFilterIndex === -1) { typeFilters.push(type); } @@ -676,10 +678,8 @@ function loaded() { } } - function onToggleTypeFilter(type) { - return function() { - toggleTypeFilter(type, true); - }; + function onToggleTypeFilter(event) { + toggleTypeFilter(this, true); } function onBodyClick(event) { From e5caf016818854b7b68be61ab2fde98837145d52 Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 22 Oct 2018 14:51:54 -0700 Subject: [PATCH 23/38] CR change for body click event --- scripts/system/html/js/entityList.js | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 2d248e48e6..a4e4c0dcd1 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -200,15 +200,16 @@ function loaded() { elDelete.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' })); } + elFilterTypeSelectBox.onclick = onToggleTypeDropdown; elFilterSearch.onkeyup = refreshEntityList; elFilterSearch.onsearch = refreshEntityList; elFilterInView.onclick = toggleFilterInView; elFilterRadius.onkeyup = onRadiusChange; elFilterRadius.onchange = onRadiusChange; + elFilterRadius.onclick = onRadiusChange; elInfoToggle.onclick = toggleInfo; // create filter type dropdown checkboxes with label and icon for each type - elFilterTypeSelectBox.onclick = toggleTypeDropdown; for (let i = 0; i < FILTER_TYPES.length; ++i) { let type = FILTER_TYPES[i]; let typeFilterID = "filter-type-" + type; @@ -225,11 +226,11 @@ function loaded() { elInput.setAttribute("filterType", type); elInput.checked = true; // all types are checked initially toggleTypeFilter(elInput, false); // add all types to the initial types filter - elInput.onclick = onToggleTypeFilter; elDiv.appendChild(elInput); elLabel.insertBefore(elSpan, elLabel.childNodes[0]); elDiv.appendChild(elLabel); elFilterTypeCheckboxes.appendChild(elDiv); + elDiv.onclick = onToggleTypeFilter; } entityList = new ListView(elEntityTableBody, elEntityTableScroll, elEntityTableHeaderRow, @@ -654,6 +655,11 @@ function loaded() { elFilterTypeCheckboxes.style.display = isTypeDropdownVisible() ? "none" : "block"; } + function onToggleTypeDropdown(event) { + toggleTypeDropdown(); + event.stopPropagation(); + } + function toggleTypeFilter(elInput, refresh) { let type = elInput.getAttribute("filterType"); let typeChecked = elInput.checked; @@ -679,14 +685,17 @@ function loaded() { } function onToggleTypeFilter(event) { - toggleTypeFilter(this, true); + let elTarget = event.target; + if (elTarget instanceof HTMLInputElement) { + toggleTypeFilter(elTarget, true); + } + event.stopPropagation(); } function onBodyClick(event) { - // if clicking anywhere outside of the type filter dropdown and it's open then close it - let elTarget = event.target; - if (isTypeDropdownVisible() && !elFilterTypeSelectBox.contains(elTarget) && - !elFilterTypeCheckboxes.contains(elTarget)) { + // if clicking anywhere outside of the type filter dropdown (since click event bubbled up to onBodyClick and + // propagation wasn't stopped by onToggleTypeFilter or onToggleTypeDropdown) and the dropdown is open then close it + if (isTypeDropdownVisible()) { toggleTypeDropdown(); } } From 91f92276864161e9ea93f73014b371b663a1d0b6 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Mon, 22 Oct 2018 15:44:16 -0700 Subject: [PATCH 24/38] Case 19446 - Update via Sandbox Popup -- opens Old Interface --- server-console/src/main.js | 4 ---- server-console/src/modules/hf-notifications.js | 12 ++++++++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/server-console/src/main.js b/server-console/src/main.js index 5c7913d775..0263f8fd65 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -877,10 +877,6 @@ function onContentLoaded() { hasShownUpdateNotification = true; } }); - notifier.on('click', function(notifierObject, options) { - log.debug("Got click", options.url); - shell.openExternal(options.url); - }); } deleteOldFiles(logPath, DELETE_LOG_FILES_OLDER_THAN_X_SECONDS, LOG_FILE_REGEX); diff --git a/server-console/src/modules/hf-notifications.js b/server-console/src/modules/hf-notifications.js index b1f337bbc3..e1ea508e47 100644 --- a/server-console/src/modules/hf-notifications.js +++ b/server-console/src/modules/hf-notifications.js @@ -5,6 +5,8 @@ const process = require('process'); const hfApp = require('./hf-app'); const path = require('path'); const AccountInfo = require('./hf-acctinfo').AccountInfo; +const url = require('url'); +const shell = require('electron').shell; const GetBuildInfo = hfApp.getBuildInfo; const buildInfo = GetBuildInfo(); const osType = os.type(); @@ -154,8 +156,14 @@ function HifiNotifications(config, menuNotificationCallback) { var _menuNotificationCallback = menuNotificationCallback; notifier.on('click', function (notifierObject, options) { - StartInterface(options.url); - _menuNotificationCallback(options.notificationType, false); + const optUrl = url.parse(options.url); + if ((optUrl.protocol === "hifi") || (optUrl.protocol === "hifiapp")) { + StartInterface(options.url); + _menuNotificationCallback(options.notificationType, false); + } + else { + shell.openExternal(options.url); + } }); } From d7dce456b269d9bc83c570292d1fb7f35445ff27 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 22 Oct 2018 18:29:27 -0700 Subject: [PATCH 25/38] Fix incorrect default entity properties in Create --- scripts/system/edit.js | 110 +++++++++-------------------------------- 1 file changed, 24 insertions(+), 86 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 71b4b2c54d..a1e8c52d1f 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -299,7 +299,6 @@ function checkEditPermissionsAndUpdate() { const DEFAULT_ENTITY_PROPERTIES = { All: { - collisionless: true, description: "", rotation: { x: 0, y: 0, z: 0, w: 1 }, collidesWith: "static,dynamic,kinematic,otherAvatar", @@ -344,12 +343,22 @@ const DEFAULT_ENTITY_PROPERTIES = { }, Text: { text: "Text", + dimensions: { + x: 0.65, + y: 0.3, + z: 0.01 + }, textColor: { red: 255, green: 255, blue: 255 }, backgroundColor: { red: 0, green: 0, blue: 0 }, lineHeight: 0.06, faceCamera: false, }, Zone: { + dimensions: { + x: 10, + y: 10, + z: 10 + }, flyingAllowed: true, ghostingAllowed: true, filter: "", @@ -359,7 +368,8 @@ const DEFAULT_ENTITY_PROPERTIES = { intensity: 1.0, direction: { x: 0.0, - y: Math.PI / 4, + y: -0.707106769084930, // 45 degrees + z: 0.7071067690849304 }, castShadows: true }, @@ -376,7 +386,7 @@ const DEFAULT_ENTITY_PROPERTIES = { hazeColor: { red: 128, green: 154, - blue: 129 + blue: 179 }, hazeBackgroundBlend: 0, hazeEnableGlare: false, @@ -389,7 +399,6 @@ const DEFAULT_ENTITY_PROPERTIES = { bloomMode: "inherit" }, Model: { - modelURL: "", collisionShape: "none", compoundShapeURL: "", animation: { @@ -413,9 +422,14 @@ const DEFAULT_ENTITY_PROPERTIES = { shapeType: "box", collisionless: true, modelURL: IMAGE_MODEL, - textures: JSON.stringify({ "tex.picture": DEFAULT_IMAGE }) + textures: JSON.stringify({ "tex.picture": "" }) }, Web: { + dimensions: { + x: 1.6, + y: 0.9, + z: 0.01 + }, sourceUrl: "https://highfidelity.com/", dpi: 30, }, @@ -462,14 +476,15 @@ const DEFAULT_ENTITY_PROPERTIES = { spinFinish: 0, spinSpread: 0, rotateWithEntity: false, - azimuthStart: 0, - azimuthFinish: 0, - polarStart: -Math.PI, - polarFinish: Math.PI + polarStart: 0, + polarFinish: 0, + azimuthStart: -Math.PI, + azimuthFinish: Math.PI }, Light: { color: { red: 255, green: 255, blue: 255 }, intensity: 5.0, + dimensions: DEFAULT_LIGHT_DIMENSIONS, falloffRadius: 1.0, isSpotlight: false, exponent: 1.0, @@ -874,14 +889,12 @@ var toolBar = (function () { addButton("newLightButton", function () { createNewEntity({ type: "Light", - dimensions: DEFAULT_LIGHT_DIMENSIONS, isSpotlight: false, color: { red: 150, green: 150, blue: 150 }, - constantAttenuation: 1, linearAttenuation: 0, quadraticAttenuation: 0, @@ -893,23 +906,6 @@ var toolBar = (function () { addButton("newTextButton", function () { createNewEntity({ type: "Text", - dimensions: { - x: 0.65, - y: 0.3, - z: 0.01 - }, - backgroundColor: { - red: 64, - green: 64, - blue: 64 - }, - textColor: { - red: 255, - green: 255, - blue: 255 - }, - text: "some text", - lineHeight: 0.06 }); }); @@ -922,76 +918,18 @@ var toolBar = (function () { addButton("newWebButton", function () { createNewEntity({ type: "Web", - dimensions: { - x: 1.6, - y: 0.9, - z: 0.01 - }, - sourceUrl: "https://highfidelity.com/" }); }); addButton("newZoneButton", function () { createNewEntity({ type: "Zone", - dimensions: { - x: 10, - y: 10, - z: 10 - } }); }); addButton("newParticleButton", function () { createNewEntity({ type: "ParticleEffect", - isEmitting: true, - emitterShouldTrail: true, - color: { - red: 200, - green: 200, - blue: 200 - }, - colorSpread: { - red: 0, - green: 0, - blue: 0 - }, - colorStart: { - red: 200, - green: 200, - blue: 200 - }, - colorFinish: { - red: 0, - green: 0, - blue: 0 - }, - emitAcceleration: { - x: -0.5, - y: 2.5, - z: -0.5 - }, - accelerationSpread: { - x: 0.5, - y: 1, - z: 0.5 - }, - emitRate: 5.5, - emitSpeed: 0, - speedSpread: 0, - lifespan: 1.5, - maxParticles: 10, - particleRadius: 0.25, - radiusStart: 0, - radiusFinish: 0.1, - radiusSpread: 0, - alpha: 0, - alphaStart: 1, - alphaFinish: 0, - polarStart: 0, - polarFinish: 0, - textures: "https://content.highfidelity.com/DomainContent/production/Particles/wispy-smoke.png" }); }); From fa67e1b269fa2c569e478fc68b42b0c8f7377dc6 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Tue, 23 Oct 2018 11:47:50 -0700 Subject: [PATCH 26/38] Remove logs and magic numbers --- interface/src/avatar/AvatarManager.cpp | 4 ---- libraries/animation/src/Rig.cpp | 5 +++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index e12ea67230..dad4c44f4b 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -127,19 +127,15 @@ void AvatarManager::setSpace(workload::SpacePointer& space ) { void AvatarManager::handleTransitAnimations(AvatarTransit::Status status) { switch (status) { case AvatarTransit::Status::STARTED: - qDebug() << "START_FRAME"; _myAvatar->getSkeletonModel()->getRig().triggerNetworkAnimation("preTransitAnim"); break; case AvatarTransit::Status::START_TRANSIT: - qDebug() << "START_TRANSIT"; _myAvatar->getSkeletonModel()->getRig().triggerNetworkAnimation("transitAnim"); break; case AvatarTransit::Status::END_TRANSIT: - qDebug() << "END_TRANSIT"; _myAvatar->getSkeletonModel()->getRig().triggerNetworkAnimation("postTransitAnim"); break; case AvatarTransit::Status::ENDED: - qDebug() << "END_FRAME"; _myAvatar->getSkeletonModel()->getRig().triggerNetworkAnimation("idleAnim"); break; case AvatarTransit::Status::PRE_TRANSIT: diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index d198550d9a..83d375b55a 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1124,17 +1124,18 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons _internalPoseSet._relativePoses = _animNode->evaluate(_animVars, context, deltaTime, triggersOut); if (_networkNode) { _networkPoseSet._relativePoses = _networkNode->evaluate(_networkVars, context, deltaTime, networkTriggersOut); + const float NETWORK_ANIMATION_BLEND_FRAMES = 6.0f; float alpha = 1.0f; std::shared_ptr clip; if (_networkAnimState.clipNodeEnum == NetworkAnimState::PreTransit) { clip = std::dynamic_pointer_cast(_networkNode->findByName("preTransitAnim")); if (clip) { - alpha = (clip->getFrame() - clip->getStartFrame()) / 6.0f; + alpha = (clip->getFrame() - clip->getStartFrame()) / NETWORK_ANIMATION_BLEND_FRAMES; } } else if (_networkAnimState.clipNodeEnum == NetworkAnimState::PostTransit) { clip = std::dynamic_pointer_cast(_networkNode->findByName("postTransitAnim")); if (clip) { - alpha = (clip->getEndFrame() - clip->getFrame()) / 6.0f; + alpha = (clip->getEndFrame() - clip->getFrame()) / NETWORK_ANIMATION_BLEND_FRAMES; } } if (_sendNetworkNode) { From be7a947ddd85689e67374d6301076e4a2c814044 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Tue, 23 Oct 2018 13:25:59 -0700 Subject: [PATCH 27/38] Fix coding-standards issue --- server-console/src/modules/hf-notifications.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server-console/src/modules/hf-notifications.js b/server-console/src/modules/hf-notifications.js index e1ea508e47..3ee2bd13a4 100644 --- a/server-console/src/modules/hf-notifications.js +++ b/server-console/src/modules/hf-notifications.js @@ -160,8 +160,7 @@ function HifiNotifications(config, menuNotificationCallback) { if ((optUrl.protocol === "hifi") || (optUrl.protocol === "hifiapp")) { StartInterface(options.url); _menuNotificationCallback(options.notificationType, false); - } - else { + } else { shell.openExternal(options.url); } }); From 4c3f5b3bbcd0326a68cdff02b07593fa9242aa88 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 11 Oct 2018 18:43:56 +0200 Subject: [PATCH 28/38] Entity List Context Menu --- scripts/system/html/css/edit-style.css | 39 +++++ scripts/system/html/entityList.html | 1 + scripts/system/html/js/entityList.js | 94 +++++++++++- .../system/html/js/entityListContextMenu.js | 143 ++++++++++++++++++ scripts/system/libraries/entityList.js | 14 +- 5 files changed, 285 insertions(+), 6 deletions(-) create mode 100644 scripts/system/html/js/entityListContextMenu.js diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index 8e7b3f1ad5..e7e577a13b 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -1801,3 +1801,42 @@ input[type=button]#export { body#entity-list-body { padding-bottom: 0; } + +.context-menu { + display: none; + position: fixed; + color: #000000; + background-color: #afafaf; + padding: 5px 0 5px 0; + cursor: default; +} +.context-menu li { + list-style-type: none; + padding: 0 5px 0 5px; + margin: 0; + white-space: nowrap; +} +.context-menu li:hover { + background-color: #e3e3e3; +} +.context-menu li.separator { + border-top: 1px solid #000000; + margin: 5px 5px; +} +.context-menu li.disabled { + color: #333333; +} +.context-menu li.separator:hover, .context-menu li.disabled:hover { + background-color: #afafaf; +} + +input.rename-entity { + height: 100%; + width: 100%; + border: none; + font-family: FiraSans-SemiBold; + font-size: 15px; + /* need this to show the text cursor when the input field is empty */ + padding-left: 2px; +} + diff --git a/scripts/system/html/entityList.html b/scripts/system/html/entityList.html index c62c785c99..ae994b56a4 100644 --- a/scripts/system/html/entityList.html +++ b/scripts/system/html/entityList.html @@ -16,6 +16,7 @@ + diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 0ced016d26..67187e6a3e 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -70,6 +70,8 @@ var selectedEntities = []; var entityList = null; // The ListView +var entityListContextMenu = new EntityListContextMenu(); + var currentSortColumn = 'type'; var currentSortOrder = ASCENDING_SORT; var isFilterInView = false; @@ -184,7 +186,83 @@ function loaded() { entityList = new ListView(elEntityTableBody, elEntityTableScroll, elEntityTableHeaderRow, createRow, updateRow, clearRow, WINDOW_NONVARIABLE_HEIGHT); - + + entityListContextMenu.initialize(); + + + function startRenamingEntity(entityID) { + if (!entitiesByID[entityID] || !entitiesByID[entityID].elRow) { + return; + } + + let elCell = entitiesByID[entityID].elRow.childNodes[COLUMN_INDEX.NAME]; + let elRenameInput = document.createElement("input"); + elRenameInput.setAttribute('class', 'rename-entity'); + elRenameInput.value = entitiesByID[entityID].name; + elRenameInput.onclick = function(event) { + event.stopPropagation(); + }; + elRenameInput.onkeyup = function(keyEvent) { + if (keyEvent.key === "Enter") { + elRenameInput.blur(); + } + }; + + elRenameInput.onblur = function(event) { + let value = elRenameInput.value; + EventBridge.emitWebEvent(JSON.stringify({ + type: 'rename', + entityID: entityID, + name: value + })); + entitiesByID[entityID].name = value; + elCell.innerText = value; + }; + + elCell.innerHTML = ""; + elCell.appendChild(elRenameInput); + + elRenameInput.select(); + } + + entityListContextMenu.setCallback(function(optionName, selectedEntityID) { + switch (optionName) { + case "Copy": + EventBridge.emitWebEvent(JSON.stringify({ type: 'copy' })); + break; + case "Paste": + EventBridge.emitWebEvent(JSON.stringify({ type: 'paste' })); + break; + case "Rename": + startRenamingEntity(selectedEntityID); + break; + case "Duplicate": + EventBridge.emitWebEvent(JSON.stringify({ type: 'duplicate' })); + break; + case "Delete": + EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' })); + break; + } + }); + + function onRowContextMenu(clickEvent) { + let entityID = this.dataset.entityID; + + if (!selectedEntities.includes(entityID)) { + let selection = [entityID]; + updateSelectedEntities(selection); + + EventBridge.emitWebEvent(JSON.stringify({ + type: "selectionUpdate", + focus: false, + entityIds: selection, + })); + + refreshFooter(); + } + entityListContextMenu.open(clickEvent, entityID); + } + function onRowClicked(clickEvent) { let entityID = this.dataset.entityID; let selection = [entityID]; @@ -221,9 +299,9 @@ function loaded() { } } } else if (!clickEvent.ctrlKey && !clickEvent.shiftKey && selectedEntities.length === 1) { - // if reselecting the same entity then deselect it + // if reselecting the same entity then start renaming it if (selectedEntities[0] === entityID) { - selection = []; + startRenamingEntity(entityID); } } @@ -502,6 +580,7 @@ function loaded() { } row.appendChild(column); } + row.oncontextmenu = onRowContextMenu; row.onclick = onRowClicked; row.ondblclick = onRowDoubleClicked; return row; @@ -672,8 +751,15 @@ function loaded() { augmentSpinButtons(); - // Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked document.addEventListener("contextmenu", function (event) { + entityListContextMenu.close.call(entityListContextMenu); + + // Disable default right-click context menu which is not visible in the HMD and makes it seem like the app has locked event.preventDefault(); }, false); + + // close context menu when switching focus to another window + $(window).blur(function(){ + entityListContextMenu.close.call(entityListContextMenu); + }); } diff --git a/scripts/system/html/js/entityListContextMenu.js b/scripts/system/html/js/entityListContextMenu.js new file mode 100644 index 0000000000..59ae2f1f73 --- /dev/null +++ b/scripts/system/html/js/entityListContextMenu.js @@ -0,0 +1,143 @@ +// +// entityListContextMenu.js +// +// exampleContextMenus.js was originally created by David Rowe on 22 Aug 2018. +// Modified to entityListContextMenu.js by Thijs Wenker on 10 Oct 2018 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +/* eslint-env browser */ +const CONTEXT_MENU_CLASS = "context-menu"; + +/** + * ContextMenu class for EntityList + * @constructor + */ +function EntityListContextMenu() { + this._elContextMenu = null; + this._callback = null; +} + +EntityListContextMenu.prototype = { + + /** + * @private + */ + _elContextMenu: null, + + /** + * @private + */ + _callback: null, + + /** + * @private + */ + _selectedEntityID: null, + + /** + * Close the context menu + */ + close: function() { + if (this.isContextMenuOpen()) { + this._elContextMenu.style.display = "none"; + } + }, + + isContextMenuOpen: function() { + return this._elContextMenu.style.display === "block"; + }, + + /** + * Open the context menu + * @param clickEvent + * @param selectedEntityID + */ + open: function(clickEvent, selectedEntityID) { + this._selectedEntityID = selectedEntityID; + // If the right-clicked item has a context menu open it. + this._elContextMenu.style.display = "block"; + this._elContextMenu.style.left + = Math.min(clickEvent.pageX, document.body.offsetWidth - this._elContextMenu.offsetWidth).toString() + "px"; + this._elContextMenu.style.top + = Math.min(clickEvent.pageY, document.body.clientHeight - this._elContextMenu.offsetHeight).toString() + "px"; + clickEvent.stopPropagation(); + }, + + /** + * Set the event callback + * @param callback + */ + setCallback: function(callback) { + this._callback = callback; + }, + + /** + * Add a labeled item to the context menu + * @param itemLabel + * @param isEnabled + * @private + */ + _addListItem: function(itemLabel, isEnabled) { + let elListItem = document.createElement("li"); + elListItem.innerText = itemLabel; + + if (isEnabled === undefined || isEnabled) { + elListItem.addEventListener("click", function () { + if (this._callback) { + this._callback.call(this, itemLabel, this._selectedEntityID); + } + }.bind(this), false); + } else { + elListItem.setAttribute('class', 'disabled'); + } + this._elContextMenu.appendChild(elListItem); + }, + + /** + * Add a separator item to the context menu + * @private + */ + _addListSeparator: function() { + let elListItem = document.createElement("li"); + elListItem.setAttribute('class', 'separator'); + this._elContextMenu.appendChild(elListItem); + }, + + /** + * Initialize the context menu. + */ + initialize: function() { + this._elContextMenu = document.createElement("ul"); + this._elContextMenu.setAttribute("class", CONTEXT_MENU_CLASS); + document.body.appendChild(this._elContextMenu); + + // TODO: enable Copy, Paste and Duplicate items once implemented + this._addListItem("Copy", false); + this._addListItem("Paste", false); + this._addListSeparator(); + this._addListItem("Rename"); + this._addListItem("Duplicate", false); + this._addListItem("Delete"); + + // Ignore clicks on context menu background or separator. + this._elContextMenu.addEventListener("click", function(event) { + // Sink clicks on context menu background or separator but let context menu item clicks through. + if (event.target.classList.contains(CONTEXT_MENU_CLASS)) { + event.stopPropagation(); + } + }); + + // Provide means to close context menu without clicking menu item. + document.body.addEventListener("click", this.close.bind(this)); + document.body.addEventListener("keydown", function(event) { + // Close context menu with Esc key. + if (this.isContextMenuOpen() && event.key === "Escape") { + this.close(); + } + }.bind(this)); + } +}; diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index 30e952723f..d3d1d76725 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -15,7 +15,7 @@ var PROFILING_ENABLED = false; var profileIndent = ''; const PROFILE_NOOP = function(_name, fn, args) { fn.apply(this, args); -} ; +}; PROFILE = !PROFILING_ENABLED ? PROFILE_NOOP : function(name, fn, args) { console.log("PROFILE-Script " + profileIndent + "(" + name + ") Begin"); var previousIndent = profileIndent; @@ -245,7 +245,7 @@ EntityListTool = function(shouldUseEditTabletApp) { Window.saveAsync("Select Where to Save", "", "*.json"); } } else if (data.type === "pal") { - var sessionIds = {}; // Collect the sessionsIds of all selected entitities, w/o duplicates. + var sessionIds = {}; // Collect the sessionsIds of all selected entities, w/o duplicates. selectionManager.selections.forEach(function (id) { var lastEditedBy = Entities.getEntityProperties(id, 'lastEditedBy').lastEditedBy; if (lastEditedBy) { @@ -271,6 +271,16 @@ EntityListTool = function(shouldUseEditTabletApp) { filterInView = data.filterInView === true; } else if (data.type === "radius") { searchRadius = data.radius; + } else if (data.type === "copy") { + Window.alert("Copy is not yet implemented."); + } else if (data.type === "paste") { + Window.alert("Paste is not yet implemented."); + } else if (data.type === "duplicate") { + Window.alert("Duplicate is not yet implemented."); + } else if (data.type === "rename") { + Entities.editEntity(data.entityID, {name: data.name}); + // make sure that the name also gets updated in the properties window + SelectionManager._update(); } }; From e5ef589a3cc24d0f18633ce7302ea7127209acf3 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 12 Oct 2018 21:15:25 +0200 Subject: [PATCH 29/38] style --- scripts/system/html/css/edit-style.css | 5 ++- scripts/system/html/js/entityList.js | 33 +++++++++----- .../system/html/js/entityListContextMenu.js | 45 ++++++++++++++----- 3 files changed, 59 insertions(+), 24 deletions(-) diff --git a/scripts/system/html/css/edit-style.css b/scripts/system/html/css/edit-style.css index e7e577a13b..956ae529ab 100644 --- a/scripts/system/html/css/edit-style.css +++ b/scripts/system/html/css/edit-style.css @@ -1812,7 +1812,7 @@ body#entity-list-body { } .context-menu li { list-style-type: none; - padding: 0 5px 0 5px; + padding: 4px 18px 4px 18px; margin: 0; white-space: nowrap; } @@ -1820,8 +1820,9 @@ body#entity-list-body { background-color: #e3e3e3; } .context-menu li.separator { - border-top: 1px solid #000000; + border-top: 1px solid #333333; margin: 5px 5px; + padding: 0 0; } .context-menu li.disabled { color: #333333; diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 67187e6a3e..749f750ecb 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -70,7 +70,10 @@ var selectedEntities = []; var entityList = null; // The ListView -var entityListContextMenu = new EntityListContextMenu(); +/** + * @type EntityListContextMenu + */ +var entityListContextMenu = null; var currentSortColumn = 'type'; var currentSortOrder = ASCENDING_SORT; @@ -187,21 +190,24 @@ function loaded() { entityList = new ListView(elEntityTableBody, elEntityTableScroll, elEntityTableHeaderRow, createRow, updateRow, clearRow, WINDOW_NONVARIABLE_HEIGHT); - entityListContextMenu.initialize(); + entityListContextMenu = new EntityListContextMenu(); function startRenamingEntity(entityID) { - if (!entitiesByID[entityID] || !entitiesByID[entityID].elRow) { + let entity = entitiesByID[entityID]; + if (!entity || entity.locked || !entity.elRow) { return; } - let elCell = entitiesByID[entityID].elRow.childNodes[COLUMN_INDEX.NAME]; + let elCell = entity.elRow.childNodes[COLUMN_INDEX.NAME]; let elRenameInput = document.createElement("input"); elRenameInput.setAttribute('class', 'rename-entity'); - elRenameInput.value = entitiesByID[entityID].name; - elRenameInput.onclick = function(event) { + elRenameInput.value = entity.name; + let ignoreClicks = function(event) { event.stopPropagation(); }; + elRenameInput.onclick = ignoreClicks; + elRenameInput.ondblclick = ignoreClicks; elRenameInput.onkeyup = function(keyEvent) { if (keyEvent.key === "Enter") { elRenameInput.blur(); @@ -215,7 +221,7 @@ function loaded() { entityID: entityID, name: value })); - entitiesByID[entityID].name = value; + entity.name = value; elCell.innerText = value; }; @@ -260,13 +266,20 @@ function loaded() { refreshFooter(); } - entityListContextMenu.open(clickEvent, entityID); + + let enabledContextMenuItems = []; + if (entitiesByID[entityID] && !entitiesByID[entityID].locked) { + enabledContextMenuItems.push('Rename'); + enabledContextMenuItems.push('Delete'); + } + + entityListContextMenu.open(clickEvent, entityID, enabledContextMenuItems); } function onRowClicked(clickEvent) { let entityID = this.dataset.entityID; let selection = [entityID]; - + if (clickEvent.ctrlKey) { let selectedIndex = selectedEntities.indexOf(entityID); if (selectedIndex >= 0) { @@ -759,7 +772,7 @@ function loaded() { }, false); // close context menu when switching focus to another window - $(window).blur(function(){ + $(window).blur(function() { entityListContextMenu.close.call(entityListContextMenu); }); } diff --git a/scripts/system/html/js/entityListContextMenu.js b/scripts/system/html/js/entityListContextMenu.js index 59ae2f1f73..62fe951ebe 100644 --- a/scripts/system/html/js/entityListContextMenu.js +++ b/scripts/system/html/js/entityListContextMenu.js @@ -19,6 +19,8 @@ const CONTEXT_MENU_CLASS = "context-menu"; function EntityListContextMenu() { this._elContextMenu = null; this._callback = null; + this._listItems = []; + this._initialize(); } EntityListContextMenu.prototype = { @@ -38,6 +40,11 @@ EntityListContextMenu.prototype = { */ _selectedEntityID: null, + /** + * @private + */ + _listItems: null, + /** * Close the context menu */ @@ -55,10 +62,17 @@ EntityListContextMenu.prototype = { * Open the context menu * @param clickEvent * @param selectedEntityID + * @param enabledOptions */ - open: function(clickEvent, selectedEntityID) { + open: function(clickEvent, selectedEntityID, enabledOptions) { this._selectedEntityID = selectedEntityID; - // If the right-clicked item has a context menu open it. + + this._listItems.forEach(function(listItem) { + let enabled = enabledOptions.includes(listItem.label); + listItem.enabled = enabled; + listItem.element.setAttribute('class', enabled ? '' : 'disabled'); + }); + this._elContextMenu.style.display = "block"; this._elContextMenu.style.left = Math.min(clickEvent.pageX, document.body.offsetWidth - this._elContextMenu.offsetWidth).toString() + "px"; @@ -85,15 +99,21 @@ EntityListContextMenu.prototype = { let elListItem = document.createElement("li"); elListItem.innerText = itemLabel; - if (isEnabled === undefined || isEnabled) { - elListItem.addEventListener("click", function () { - if (this._callback) { - this._callback.call(this, itemLabel, this._selectedEntityID); - } - }.bind(this), false); - } else { - elListItem.setAttribute('class', 'disabled'); - } + let listItem = { + label: itemLabel, + element: elListItem, + enabled: false + }; + + elListItem.addEventListener("click", function () { + if (listItem.enabled && this._callback) { + this._callback.call(this, itemLabel, this._selectedEntityID); + } + }.bind(this), false); + + elListItem.setAttribute('class', 'disabled'); + + this._listItems.push(listItem); this._elContextMenu.appendChild(elListItem); }, @@ -109,8 +129,9 @@ EntityListContextMenu.prototype = { /** * Initialize the context menu. + * @private */ - initialize: function() { + _initialize: function() { this._elContextMenu = document.createElement("ul"); this._elContextMenu.setAttribute("class", CONTEXT_MENU_CLASS); document.body.appendChild(this._elContextMenu); From 4069ca2e0c39a51d1b3eb911d62a19439c3c0b9d Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 20 Oct 2018 02:43:41 +0200 Subject: [PATCH 30/38] CR feedback --- scripts/system/html/js/entityList.js | 11 ++++---- .../system/html/js/entityListContextMenu.js | 27 +++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 749f750ecb..663acb758c 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -231,8 +231,11 @@ function loaded() { elRenameInput.select(); } - entityListContextMenu.setCallback(function(optionName, selectedEntityID) { + entityListContextMenu.setOnSelectedCallback(function(optionName, selectedEntityID) { switch (optionName) { + case "Cut": + EventBridge.emitWebEvent(JSON.stringify({ type: 'cut' })); + break; case "Copy": EventBridge.emitWebEvent(JSON.stringify({ type: 'copy' })); break; @@ -263,8 +266,6 @@ function loaded() { focus: false, entityIds: selection, })); - - refreshFooter(); } let enabledContextMenuItems = []; @@ -765,7 +766,7 @@ function loaded() { augmentSpinButtons(); document.addEventListener("contextmenu", function (event) { - entityListContextMenu.close.call(entityListContextMenu); + entityListContextMenu.close(); // Disable default right-click context menu which is not visible in the HMD and makes it seem like the app has locked event.preventDefault(); @@ -773,6 +774,6 @@ function loaded() { // close context menu when switching focus to another window $(window).blur(function() { - entityListContextMenu.close.call(entityListContextMenu); + entityListContextMenu.close(); }); } diff --git a/scripts/system/html/js/entityListContextMenu.js b/scripts/system/html/js/entityListContextMenu.js index 62fe951ebe..d71719f252 100644 --- a/scripts/system/html/js/entityListContextMenu.js +++ b/scripts/system/html/js/entityListContextMenu.js @@ -18,7 +18,7 @@ const CONTEXT_MENU_CLASS = "context-menu"; */ function EntityListContextMenu() { this._elContextMenu = null; - this._callback = null; + this._onSelectedCallback = null; this._listItems = []; this._initialize(); } @@ -33,7 +33,7 @@ EntityListContextMenu.prototype = { /** * @private */ - _callback: null, + _onSelectedCallback: null, /** * @private @@ -82,20 +82,19 @@ EntityListContextMenu.prototype = { }, /** - * Set the event callback - * @param callback + * Set the callback for when a menu item is selected + * @param onSelectedCallback */ - setCallback: function(callback) { - this._callback = callback; + setOnSelectedCallback: function(onSelectedCallback) { + this._onSelectedCallback = onSelectedCallback; }, /** * Add a labeled item to the context menu * @param itemLabel - * @param isEnabled * @private */ - _addListItem: function(itemLabel, isEnabled) { + _addListItem: function(itemLabel) { let elListItem = document.createElement("li"); elListItem.innerText = itemLabel; @@ -106,8 +105,8 @@ EntityListContextMenu.prototype = { }; elListItem.addEventListener("click", function () { - if (listItem.enabled && this._callback) { - this._callback.call(this, itemLabel, this._selectedEntityID); + if (listItem.enabled && this._onSelectedCallback) { + this._onSelectedCallback.call(this, itemLabel, this._selectedEntityID); } }.bind(this), false); @@ -136,12 +135,12 @@ EntityListContextMenu.prototype = { this._elContextMenu.setAttribute("class", CONTEXT_MENU_CLASS); document.body.appendChild(this._elContextMenu); - // TODO: enable Copy, Paste and Duplicate items once implemented - this._addListItem("Copy", false); - this._addListItem("Paste", false); + this._addListItem("Cut"); + this._addListItem("Copy"); + this._addListItem("Paste"); this._addListSeparator(); this._addListItem("Rename"); - this._addListItem("Duplicate", false); + this._addListItem("Duplicate"); this._addListItem("Delete"); // Ignore clicks on context menu background or separator. From 3fa4ee0e1ddede8e7905200a179645c750f8cfc8 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Sat, 20 Oct 2018 03:25:52 +0200 Subject: [PATCH 31/38] enable duplicate copy cut paste functionality of context menu --- scripts/system/html/js/entityList.js | 3 ++- scripts/system/libraries/entityList.js | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 663acb758c..d01e68cff2 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -268,8 +268,9 @@ function loaded() { })); } - let enabledContextMenuItems = []; + let enabledContextMenuItems = ['Copy', 'Paste', 'Duplicate']; if (entitiesByID[entityID] && !entitiesByID[entityID].locked) { + enabledContextMenuItems.push('Cut'); enabledContextMenuItems.push('Rename'); enabledContextMenuItems.push('Delete'); } diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index d3d1d76725..3d73f2f115 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -271,12 +271,15 @@ EntityListTool = function(shouldUseEditTabletApp) { filterInView = data.filterInView === true; } else if (data.type === "radius") { searchRadius = data.radius; + } else if (data.type === "cut") { + cutSelectedEntities(); } else if (data.type === "copy") { - Window.alert("Copy is not yet implemented."); + copySelectedEntities(); } else if (data.type === "paste") { - Window.alert("Paste is not yet implemented."); + pasteEntities(); } else if (data.type === "duplicate") { - Window.alert("Duplicate is not yet implemented."); + SelectionManager.duplicateSelection(); + that.sendUpdate(); } else if (data.type === "rename") { Entities.editEntity(data.entityID, {name: data.name}); // make sure that the name also gets updated in the properties window From 4e9f9b3edf9960af9ea82eb784c7cecd631c94e2 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 22 Oct 2018 22:39:40 +0200 Subject: [PATCH 32/38] comply with copy/paste update --- scripts/system/libraries/entityList.js | 6 +++--- scripts/system/libraries/entitySelectionTool.js | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index 3d73f2f115..da31ef8463 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -272,11 +272,11 @@ EntityListTool = function(shouldUseEditTabletApp) { } else if (data.type === "radius") { searchRadius = data.radius; } else if (data.type === "cut") { - cutSelectedEntities(); + SelectionManager.cutSelectedEntities(); } else if (data.type === "copy") { - copySelectedEntities(); + SelectionManager.copySelectedEntities(); } else if (data.type === "paste") { - pasteEntities(); + SelectionManager.pasteEntities(); } else if (data.type === "duplicate") { SelectionManager.duplicateSelection(); that.sendUpdate(); diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 843d3e986f..a117df6fc7 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -353,12 +353,12 @@ SelectionManager = (function() { } return createdEntityIDs; - } + }; that.cutSelectedEntities = function() { - copySelectedEntities(); + that.copySelectedEntities(); deleteSelectedEntities(); - } + }; that.copySelectedEntities = function() { var entityProperties = Entities.getMultipleEntityProperties(that.selections); @@ -434,7 +434,7 @@ SelectionManager = (function() { z: brn.z + entityClipboard.dimensions.z / 2 }; } - } + }; that.pasteEntities = function() { var dimensions = entityClipboard.dimensions; @@ -442,7 +442,7 @@ SelectionManager = (function() { var pastePosition = getPositionToCreateEntity(maxDimension); var deltaPosition = Vec3.subtract(pastePosition, entityClipboard.position); - var copiedProperties = [] + var copiedProperties = []; var ids = []; entityClipboard.entities.forEach(function(originalProperties) { var properties = deepCopy(originalProperties); @@ -475,7 +475,7 @@ SelectionManager = (function() { redo(copiedProperties); undoHistory.pushCommand(undo, copiedProperties, redo, copiedProperties); - } + }; that._update = function(selectionUpdated) { var properties = null; From 10ee5ac5b422144a8ac443233f1dcb3ff75f6b02 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Tue, 23 Oct 2018 14:46:13 -0700 Subject: [PATCH 33/38] URL protocol returns the protocol string with the ':' attached (how did this work before?!) --- server-console/src/modules/hf-notifications.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-console/src/modules/hf-notifications.js b/server-console/src/modules/hf-notifications.js index 3ee2bd13a4..8a812625b4 100644 --- a/server-console/src/modules/hf-notifications.js +++ b/server-console/src/modules/hf-notifications.js @@ -157,7 +157,7 @@ function HifiNotifications(config, menuNotificationCallback) { var _menuNotificationCallback = menuNotificationCallback; notifier.on('click', function (notifierObject, options) { const optUrl = url.parse(options.url); - if ((optUrl.protocol === "hifi") || (optUrl.protocol === "hifiapp")) { + if ((optUrl.protocol === "hifi:") || (optUrl.protocol === "hifiapp:")) { StartInterface(options.url); _menuNotificationCallback(options.notificationType, false); } else { From 28c062b842e0883a332568e37cbcdb8e408fc77a Mon Sep 17 00:00:00 2001 From: Gabriel Calero Date: Wed, 24 Oct 2018 11:57:59 -0300 Subject: [PATCH 34/38] Don't show stats in release builds --- interface/resources/qml/+android/Stats.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/+android/Stats.qml b/interface/resources/qml/+android/Stats.qml index 0dcb07e730..e9a2aa47eb 100644 --- a/interface/resources/qml/+android/Stats.qml +++ b/interface/resources/qml/+android/Stats.qml @@ -10,6 +10,7 @@ Item { property int modality: Qt.NonModal implicitHeight: row.height implicitWidth: row.width + visible: false Component.onCompleted: { stats.parentChanged.connect(fill); From 27e0c85b01d799ac4268b82fad863b21bcf57e88 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Fri, 5 Oct 2018 22:13:45 +0200 Subject: [PATCH 35/38] entity list auto scroll --- scripts/system/html/js/entityList.js | 35 +++++++++++++++---- scripts/system/html/js/listView.js | 18 ++++++++++ scripts/system/libraries/entityList.js | 7 ++-- .../system/libraries/entitySelectionTool.js | 8 ++--- 4 files changed, 55 insertions(+), 13 deletions(-) diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 0ced016d26..0c87470f57 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -194,7 +194,7 @@ function loaded() { if (selectedIndex >= 0) { selection = []; selection = selection.concat(selectedEntities); - selection.splice(selectedIndex, 1) + selection.splice(selectedIndex, 1); } else { selection = selection.concat(selectedEntities); } @@ -453,7 +453,10 @@ function loaded() { } } - function updateSelectedEntities(selectedIDs) { + function updateSelectedEntities(selectedIDs, autoScroll) { + // force autoScroll to be a boolean + autoScroll = !!autoScroll; + let notFound = false; // reset all currently selected entities and their rows first @@ -482,6 +485,26 @@ function loaded() { } }); + if (autoScroll && selectedIDs.length > 0) { + let firstItem = Number.MAX_VALUE; + let lastItem = -1; + let itemFound = false; + visibleEntities.forEach(function(entity, index) { + if (selectedIDs.indexOf(entity.id) !== -1) { + if (firstItem > index) { + firstItem = index; + } + if (lastItem < index) { + lastItem = index; + } + itemFound = true; + } + }); + if (itemFound) { + entityList.scrollToRow(firstItem, lastItem); + } + } + refreshFooter(); return notFound; @@ -640,8 +663,8 @@ function loaded() { data = JSON.parse(data); if (data.type === "clearEntityList") { clearEntities(); - } else if (data.type === "selectionUpdate") { - let notFound = updateSelectedEntities(data.selectedIDs); + } else if (data.type === "selectionUpdate" && data.caller !== "entityList") { + let notFound = updateSelectedEntities(data.selectedIDs, true); if (notFound) { refreshEntities(); } @@ -653,13 +676,13 @@ function loaded() { clearEntities(); } else { updateEntityData(newEntities); - updateSelectedEntities(data.selectedIDs); + updateSelectedEntities(data.selectedIDs, true); } } }); } else if (data.type === "removeEntities" && data.deletedIDs !== undefined && data.selectedIDs !== undefined) { removeEntities(data.deletedIDs); - updateSelectedEntities(data.selectedIDs); + updateSelectedEntities(data.selectedIDs, true); } else if (data.type === "deleted" && data.ids) { removeEntities(data.ids); } diff --git a/scripts/system/html/js/listView.js b/scripts/system/html/js/listView.js index 10dc37efba..d11452e111 100644 --- a/scripts/system/html/js/listView.js +++ b/scripts/system/html/js/listView.js @@ -152,6 +152,24 @@ ListView.prototype = { this.refresh(); } }, + + scrollToRow(rowIndex, lastRowIndex) { + lastRowIndex = lastRowIndex ? lastRowIndex : rowIndex; + let boundingTop = rowIndex * this.rowHeight; + let boundingBottom = (lastRowIndex * this.rowHeight) + this.rowHeight; + if ((boundingBottom - boundingTop) > this.elTableScroll.clientHeight) { + boundingBottom = boundingTop + this.elTableScroll.clientHeight; + } + + let currentVisibleAreaTop = this.elTableScroll.scrollTop; + let currentVisibleAreaBottom = currentVisibleAreaTop + this.elTableScroll.clientHeight; + + if (boundingTop < currentVisibleAreaTop) { + this.elTableScroll.scrollTop = boundingTop; + } else if (boundingBottom > currentVisibleAreaBottom) { + this.elTableScroll.scrollTop = boundingBottom - (this.elTableScroll.clientHeight); + } + }, refresh: function() { // block refreshing before rows are initialized diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index 30e952723f..04b19aa280 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -98,7 +98,7 @@ EntityListTool = function(shouldUseEditTabletApp) { that.setVisible(!visible); }; - selectionManager.addEventListener(function() { + selectionManager.addEventListener(function(isSelectionUpdate, caller) { var selectedIDs = []; for (var i = 0; i < selectionManager.selections.length; i++) { @@ -107,7 +107,8 @@ EntityListTool = function(shouldUseEditTabletApp) { emitJSONScriptEvent({ type: 'selectionUpdate', - selectedIDs: selectedIDs + selectedIDs: selectedIDs, + caller: caller }); }); @@ -224,7 +225,7 @@ EntityListTool = function(shouldUseEditTabletApp) { for (var i = 0; i < ids.length; i++) { entityIDs.push(ids[i]); } - selectionManager.setSelections(entityIDs); + selectionManager.setSelections(entityIDs, "entityList"); if (data.focus) { cameraManager.enable(); cameraManager.focus(selectionManager.worldPosition, diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 843d3e986f..4302a531b5 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -136,7 +136,7 @@ SelectionManager = (function() { return that.selections.length > 0; }; - that.setSelections = function(entityIDs) { + that.setSelections = function(entityIDs, caller) { that.selections = []; for (var i = 0; i < entityIDs.length; i++) { var entityID = entityIDs[i]; @@ -144,7 +144,7 @@ SelectionManager = (function() { Selection.addToSelectedItemsList(HIGHLIGHT_LIST_NAME, "entity", entityID); } - that._update(true); + that._update(true, caller); }; that.addEntity = function(entityID, toggleSelection) { @@ -477,7 +477,7 @@ SelectionManager = (function() { undoHistory.pushCommand(undo, copiedProperties, redo, copiedProperties); } - that._update = function(selectionUpdated) { + that._update = function(selectionUpdated, caller) { var properties = null; if (that.selections.length === 0) { that.localDimensions = null; @@ -542,7 +542,7 @@ SelectionManager = (function() { for (var j = 0; j < listeners.length; j++) { try { - listeners[j](selectionUpdated === true); + listeners[j](selectionUpdated === true, caller); } catch (e) { print("ERROR: entitySelectionTool.update got exception: " + JSON.stringify(e)); } From 776b8b8fc7161060b037e12e661dedf122897235 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 9 Oct 2018 00:59:53 +0200 Subject: [PATCH 36/38] CR and style fixes --- scripts/system/edit.js | 42 +++++++++---------- scripts/system/html/js/entityList.js | 29 ++++++------- scripts/system/html/js/listView.js | 14 +++++-- scripts/system/libraries/entityList.js | 9 ++-- .../system/libraries/entitySelectionTool.js | 38 ++++++++--------- 5 files changed, 69 insertions(+), 63 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index b911541f79..0d63b92b63 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -385,7 +385,7 @@ var toolBar = (function () { Entities.editEntity(entityID, { position: position }); - selectionManager._update(); + selectionManager._update(false, this); } else if (dimensionsCheckCount < MAX_DIMENSIONS_CHECKS) { Script.setTimeout(dimensionsCheckFunction, DIMENSIONS_CHECK_INTERVAL); } @@ -397,9 +397,9 @@ var toolBar = (function () { properties.type + " would be out of bounds."); } - selectionManager.clearSelections(); + selectionManager.clearSelections(this); entityListTool.sendUpdate(); - selectionManager.setSelections([entityID]); + selectionManager.setSelections([entityID], this); Window.setFocus(); @@ -550,7 +550,7 @@ var toolBar = (function () { } deletedEntityTimer = Script.setTimeout(function () { if (entitiesToDelete.length > 0) { - selectionManager.removeEntities(entitiesToDelete); + selectionManager.removeEntities(entitiesToDelete, this); } entityListTool.removeEntities(entitiesToDelete, selectionManager.selections); entitiesToDelete = []; @@ -866,7 +866,7 @@ var toolBar = (function () { gridTool.setVisible(false); grid.setEnabled(false); propertiesTool.setVisible(false); - selectionManager.clearSelections(); + selectionManager.clearSelections(this); cameraManager.disable(); selectionDisplay.disableTriggerMapping(); tablet.landscape = false; @@ -994,7 +994,7 @@ function handleOverlaySelectionToolUpdates(channel, message, sender) { var entity = entityIconOverlayManager.findEntity(data.overlayID); if (entity !== null) { - selectionManager.setSelections([entity]); + selectionManager.setSelections([entity], this); } } } @@ -1141,7 +1141,7 @@ function mouseClickEvent(event) { if (result === null || result === undefined) { if (!event.isShifted) { - selectionManager.clearSelections(); + selectionManager.clearSelections(this); } return; } @@ -1193,9 +1193,9 @@ function mouseClickEvent(event) { } if (!event.isShifted) { - selectionManager.setSelections([foundEntity]); + selectionManager.setSelections([foundEntity], this); } else { - selectionManager.addEntity(foundEntity, true); + selectionManager.addEntity(foundEntity, true, this); } if (wantDebug) { @@ -1493,7 +1493,7 @@ function selectAllEtitiesInCurrentSelectionBox(keepIfTouching) { } } } - selectionManager.setSelections(entities); + selectionManager.setSelections(entities, this); } } @@ -1633,7 +1633,7 @@ function deleteSelectedEntities() { } if (savedProperties.length > 0) { - SelectionManager.clearSelections(); + SelectionManager.clearSelections(this); pushCommandForSelections([], savedProperties); entityListTool.deleteEntities(deletedIDs); } @@ -1650,7 +1650,7 @@ function toggleSelectedEntitiesLocked() { }); } entityListTool.sendUpdate(); - selectionManager._update(); + selectionManager._update(false, this); } } @@ -1664,7 +1664,7 @@ function toggleSelectedEntitiesVisible() { }); } entityListTool.sendUpdate(); - selectionManager._update(); + selectionManager._update(false, this); } } @@ -1861,7 +1861,7 @@ function importSVO(importURL) { } if (isActive) { - selectionManager.setSelections(pastedEntityIDs); + selectionManager.setSelections(pastedEntityIDs, this); } } else { Window.notifyEditError("Can't import entities: entities would be out of bounds."); @@ -1909,7 +1909,7 @@ function deleteKey(value) { } function deselectKey(value) { if (value === 0) { // on release - selectionManager.clearSelections(); + selectionManager.clearSelections(this); } } function toggleKey(value) { @@ -2069,7 +2069,7 @@ function applyEntityProperties(data) { // We might be getting an undo while edit.js is disabled. If that is the case, don't set // our selections, causing the edit widgets to display. if (isActive) { - selectionManager.setSelections(selectedEntityIDs); + selectionManager.setSelections(selectedEntityIDs, this); } } @@ -2272,7 +2272,7 @@ var PropertiesTool = function (opts) { } } pushCommandForSelections(); - selectionManager._update(); + selectionManager._update(false, this); } else if (data.type === 'parent') { parentSelectedEntities(); } else if (data.type === 'unparent') { @@ -2301,7 +2301,7 @@ var PropertiesTool = function (opts) { }); } pushCommandForSelections(); - selectionManager._update(); + selectionManager._update(false, this); } } else if (data.action === "moveAllToGrid") { if (selectionManager.hasSelection()) { @@ -2321,7 +2321,7 @@ var PropertiesTool = function (opts) { }); } pushCommandForSelections(); - selectionManager._update(); + selectionManager._update(false, this); } } else if (data.action === "resetToNaturalDimensions") { if (selectionManager.hasSelection()) { @@ -2342,7 +2342,7 @@ var PropertiesTool = function (opts) { } } pushCommandForSelections(); - selectionManager._update(); + selectionManager._update(false, this); } } else if (data.action === "previewCamera") { if (selectionManager.hasSelection()) { @@ -2360,7 +2360,7 @@ var PropertiesTool = function (opts) { }); } pushCommandForSelections(); - selectionManager._update(); + selectionManager._update(false, this); } } else if (data.action === "reloadClientScripts") { if (selectionManager.hasSelection()) { diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 0c87470f57..a75bd7e4b9 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -13,7 +13,7 @@ const DESCENDING_STRING = '▾'; const LOCKED_GLYPH = ""; const VISIBLE_GLYPH = ""; const TRANSPARENCY_GLYPH = ""; -const BAKED_GLYPH = "" +const BAKED_GLYPH = ""; const SCRIPT_GLYPH = "k"; const BYTES_PER_MEGABYTE = 1024 * 1024; const IMAGE_MODEL_NAME = 'default-image-model.fbx'; @@ -54,10 +54,10 @@ const COMPARE_ASCENDING = function(a, b) { } return 1; -} +}; const COMPARE_DESCENDING = function(a, b) { return COMPARE_ASCENDING(b, a); -} +}; // List of all entities var entities = []; @@ -156,22 +156,22 @@ function loaded() { }; elRefresh.onclick = function() { refreshEntities(); - } + }; elToggleLocked.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'toggleLocked' })); - } + }; elToggleVisible.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'toggleVisible' })); - } + }; elExport.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'export'})); - } + }; elPal.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'pal' })); - } + }; elDelete.onclick = function() { EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' })); - } + }; elFilter.onkeyup = refreshEntityList; elFilter.onpaste = refreshEntityList; elFilter.onchange = onFilterChange; @@ -227,7 +227,7 @@ function loaded() { } } - updateSelectedEntities(selection); + updateSelectedEntities(selection, false); EventBridge.emitWebEvent(JSON.stringify({ type: "selectionUpdate", @@ -289,7 +289,7 @@ function loaded() { hasScript: entity.hasScript, elRow: null, // if this entity has a visible row element assigned to it selected: false // if this entity is selected for edit regardless of having a visible row - } + }; entities.push(entityData); entitiesByID[entityData.id] = entityData; @@ -418,7 +418,7 @@ function loaded() { isBaked: document.querySelector('#entity-isBaked .sort-order'), drawCalls: document.querySelector('#entity-drawCalls .sort-order'), hasScript: document.querySelector('#entity-hasScript .sort-order'), - } + }; function setSortColumn(column) { PROFILE("set-sort-column", function() { if (currentSortColumn === column) { @@ -454,9 +454,6 @@ function loaded() { } function updateSelectedEntities(selectedIDs, autoScroll) { - // force autoScroll to be a boolean - autoScroll = !!autoScroll; - let notFound = false; // reset all currently selected entities and their rows first @@ -663,7 +660,7 @@ function loaded() { data = JSON.parse(data); if (data.type === "clearEntityList") { clearEntities(); - } else if (data.type === "selectionUpdate" && data.caller !== "entityList") { + } else if (data.type === "selectionUpdate") { let notFound = updateSelectedEntities(data.selectedIDs, true); if (notFound) { refreshEntities(); diff --git a/scripts/system/html/js/listView.js b/scripts/system/html/js/listView.js index d11452e111..62163ffe16 100644 --- a/scripts/system/html/js/listView.js +++ b/scripts/system/html/js/listView.js @@ -38,7 +38,7 @@ function ListView(elTableBody, elTableScroll, elTableHeaderRow, createRowFunctio this.lastRowShiftScrollTop = 0; this.initialize(); -}; +} ListView.prototype = { getNumRows: function() { @@ -153,9 +153,15 @@ ListView.prototype = { } }, - scrollToRow(rowIndex, lastRowIndex) { - lastRowIndex = lastRowIndex ? lastRowIndex : rowIndex; - let boundingTop = rowIndex * this.rowHeight; + /** + * Scrolls firstRowIndex with least effort, also tries to make the window include the other selections in case lastRowIndex is set. + * In the case that firstRowIndex and lastRowIndex are already within the visible bounds then nothing will happen. + * @param {number} firstRowIndex - The row that will be scrolled to. + * @param {number} lastRowIndex - The last index of the bound. + */ + scrollToRow: function (firstRowIndex, lastRowIndex) { + lastRowIndex = lastRowIndex ? lastRowIndex : firstRowIndex; + let boundingTop = firstRowIndex * this.rowHeight; let boundingBottom = (lastRowIndex * this.rowHeight) + this.rowHeight; if ((boundingBottom - boundingTop) > this.elTableScroll.clientHeight) { boundingBottom = boundingTop + this.elTableScroll.clientHeight; diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index 04b19aa280..045990b99b 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -99,6 +99,10 @@ EntityListTool = function(shouldUseEditTabletApp) { }; selectionManager.addEventListener(function(isSelectionUpdate, caller) { + if (caller === that) { + // ignore events that we emitted from the entity list itself + return; + } var selectedIDs = []; for (var i = 0; i < selectionManager.selections.length; i++) { @@ -107,8 +111,7 @@ EntityListTool = function(shouldUseEditTabletApp) { emitJSONScriptEvent({ type: 'selectionUpdate', - selectedIDs: selectedIDs, - caller: caller + selectedIDs: selectedIDs }); }); @@ -225,7 +228,7 @@ EntityListTool = function(shouldUseEditTabletApp) { for (var i = 0; i < ids.length; i++) { entityIDs.push(ids[i]); } - selectionManager.setSelections(entityIDs, "entityList"); + selectionManager.setSelections(entityIDs, that); if (data.focus) { cameraManager.enable(); cameraManager.focus(selectionManager.worldPosition, diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 4302a531b5..73d84cf293 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -40,7 +40,7 @@ SelectionManager = (function() { Messages.messageReceived.connect(handleEntitySelectionToolUpdates); } - // FUNCTION: HANDLE ENTITY SELECTION TOOL UDPATES + // FUNCTION: HANDLE ENTITY SELECTION TOOL UPDATES function handleEntitySelectionToolUpdates(channel, message, sender) { if (channel !== 'entityToolUpdates') { return; @@ -63,7 +63,7 @@ SelectionManager = (function() { if (wantDebug) { print("setting selection to " + messageParsed.entityID); } - that.setSelections([messageParsed.entityID]); + that.setSelections([messageParsed.entityID], that); } } else if (messageParsed.method === "clearSelection") { if (!SelectionDisplay.triggered() || SelectionDisplay.triggeredHand === messageParsed.hand) { @@ -147,7 +147,7 @@ SelectionManager = (function() { that._update(true, caller); }; - that.addEntity = function(entityID, toggleSelection) { + that.addEntity = function(entityID, toggleSelection, caller) { if (entityID) { var idx = -1; for (var i = 0; i < that.selections.length; i++) { @@ -165,7 +165,7 @@ SelectionManager = (function() { } } - that._update(true); + that._update(true, caller); }; function removeEntityByID(entityID) { @@ -176,21 +176,21 @@ SelectionManager = (function() { } } - that.removeEntity = function (entityID) { + that.removeEntity = function (entityID, caller) { removeEntityByID(entityID); - that._update(true); + that._update(true, caller); }; - that.removeEntities = function(entityIDs) { + that.removeEntities = function(entityIDs, caller) { for (var i = 0, length = entityIDs.length; i < length; i++) { removeEntityByID(entityIDs[i]); } - that._update(true); + that._update(true, caller); }; - that.clearSelections = function() { + that.clearSelections = function(caller) { that.selections = []; - that._update(true); + that._update(true, caller); }; that.addChildrenEntities = function(parentEntityID, entityList) { @@ -985,7 +985,7 @@ SelectionDisplay = (function() { that.pressedHand = NO_HAND; that.triggered = function() { return that.triggeredHand !== NO_HAND; - } + }; function pointingAtDesktopWindowOrTablet(hand) { var pointingAtDesktopWindow = (hand === Controller.Standard.RightHand && SelectionManager.pointingAtDesktopWindowRight) || @@ -1032,7 +1032,7 @@ SelectionDisplay = (function() { that.disableTriggerMapping = function() { that.triggerClickMapping.disable(); that.triggerPressMapping.disable(); - } + }; Script.scriptEnding.connect(that.disableTriggerMapping); // FUNCTION DEF(s): Intersection Check Helpers @@ -1234,7 +1234,7 @@ SelectionDisplay = (function() { if (wantDebug) { print(" Trigger SelectionManager::update"); } - SelectionManager._update(); + SelectionManager._update(false, that); if (wantDebug) { print("=============== eST::MouseMoveEvent END ======================="); @@ -1299,7 +1299,7 @@ SelectionDisplay = (function() { lastMouseEvent.isControl = event.isControl; lastMouseEvent.isAlt = event.isAlt; activeTool.onMove(lastMouseEvent); - SelectionManager._update(); + SelectionManager._update(false, this); } }; @@ -1315,7 +1315,7 @@ SelectionDisplay = (function() { lastMouseEvent.isControl = event.isControl; lastMouseEvent.isAlt = event.isAlt; activeTool.onMove(lastMouseEvent); - SelectionManager._update(); + SelectionManager._update(false, this); } }; @@ -2179,7 +2179,7 @@ SelectionDisplay = (function() { } } - SelectionManager._update(); + SelectionManager._update(false, this); } }); } @@ -2301,7 +2301,7 @@ SelectionDisplay = (function() { previousPickRay = pickRay; - SelectionManager._update(); + SelectionManager._update(false, this); } }); } @@ -2488,7 +2488,7 @@ SelectionDisplay = (function() { previousPickRay = pickRay; - SelectionManager._update(); + SelectionManager._update(false, this); } }); } @@ -2599,7 +2599,7 @@ SelectionDisplay = (function() { previousPickRay = pickRay; - SelectionManager._update(); + SelectionManager._update(false, this); } }); } From 8e1cfb5a067bde0d5676ba3d1024d53cc17ba4ec Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 22 Oct 2018 23:03:26 +0200 Subject: [PATCH 37/38] fix double click selection --- scripts/system/html/js/entityList.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index a75bd7e4b9..55871533e2 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -239,11 +239,16 @@ function loaded() { } function onRowDoubleClicked() { + let selection = [this.dataset.entityID]; + updateSelectedEntities(selection, false); + EventBridge.emitWebEvent(JSON.stringify({ type: "selectionUpdate", focus: true, - entityIds: [this.dataset.entityID], + entityIds: selection, })); + + refreshFooter(); } function decimalMegabytes(number) { From 5f35a324805d68c97748f5ac65c8b7e97282e15d Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Mon, 22 Oct 2018 23:09:41 +0200 Subject: [PATCH 38/38] remove duplicate calls to refresh footer --- scripts/system/html/js/entityList.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 55871533e2..62c8d6ca6f 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -234,8 +234,6 @@ function loaded() { focus: false, entityIds: selection, })); - - refreshFooter(); } function onRowDoubleClicked() { @@ -247,8 +245,6 @@ function loaded() { focus: true, entityIds: selection, })); - - refreshFooter(); } function decimalMegabytes(number) {