diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 9c03bdd3bb..e7d86c824e 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -235,7 +235,8 @@ void updateConsumedCores() { AssetServer::AssetServer(ReceivedMessage& message) : ThreadedAssignment(message), _transferTaskPool(this), - _bakingTaskPool(this) + _bakingTaskPool(this), + _filesizeLimit(MAX_UPLOAD_SIZE) { // store the current state of image compression so we can reset it when this assignment is complete _wasColorTextureCompressionEnabled = image::isColorTexturesCompressionEnabled(); @@ -343,8 +344,8 @@ void AssetServer::completeSetup() { auto maxBandwidthValue = assetServerObject[MAX_BANDWIDTH_OPTION]; auto maxBandwidthFloat = maxBandwidthValue.toDouble(-1); + const int BITS_PER_MEGABITS = 1000 * 1000; if (maxBandwidthFloat > 0.0) { - const int BITS_PER_MEGABITS = 1000 * 1000; int maxBandwidth = maxBandwidthFloat * BITS_PER_MEGABITS; nodeList->setConnectionMaxBandwidth(maxBandwidth); qCInfo(asset_server) << "Set maximum bandwith per connection to" << maxBandwidthFloat << "Mb/s." @@ -406,6 +407,15 @@ void AssetServer::completeSetup() { qCCritical(asset_server) << "Asset Server assignment will not continue because mapping file could not be loaded."; setFinished(true); } + + // get file size limit for an asset + static const QString ASSETS_FILESIZE_LIMIT_OPTION = "assets_filesize_limit"; + auto assetsFilesizeLimitJSONValue = assetServerObject[ASSETS_FILESIZE_LIMIT_OPTION]; + auto assetsFilesizeLimit = (uint64_t)assetsFilesizeLimitJSONValue.toInt(MAX_UPLOAD_SIZE); + + if (assetsFilesizeLimit != 0 && assetsFilesizeLimit < MAX_UPLOAD_SIZE) { + _filesizeLimit = assetsFilesizeLimit * BITS_PER_MEGABITS; + } } void AssetServer::cleanupUnmappedFiles() { @@ -730,7 +740,7 @@ void AssetServer::handleAssetUpload(QSharedPointer message, Sha if (senderNode->getCanWriteToAssetServer()) { qCDebug(asset_server) << "Starting an UploadAssetTask for upload from" << uuidStringWithoutCurlyBraces(senderNode->getUUID()); - auto task = new UploadAssetTask(message, senderNode, _filesDirectory); + auto task = new UploadAssetTask(message, senderNode, _filesDirectory, _filesizeLimit); _transferTaskPool.start(task); } else { // this is a node the domain told us is not allowed to rez entities diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index aeb40a416f..e6393e6a98 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -127,6 +127,8 @@ private: bool _wasGrayscaleTextureCompressionEnabled { false }; bool _wasNormalTextureCompressionEnabled { false }; bool _wasCubeTextureCompressionEnabled { false }; + + uint64_t _filesizeLimit; }; #endif diff --git a/assignment-client/src/assets/UploadAssetTask.cpp b/assignment-client/src/assets/UploadAssetTask.cpp index 7e8e94c34d..5e6d59d032 100644 --- a/assignment-client/src/assets/UploadAssetTask.cpp +++ b/assignment-client/src/assets/UploadAssetTask.cpp @@ -22,10 +22,11 @@ UploadAssetTask::UploadAssetTask(QSharedPointer receivedMessage, SharedNodePointer senderNode, - const QDir& resourcesDir) : + const QDir& resourcesDir, uint64_t filesizeLimit) : _receivedMessage(receivedMessage), _senderNode(senderNode), - _resourcesDir(resourcesDir) + _resourcesDir(resourcesDir), + _filesizeLimit(filesizeLimit) { } @@ -48,7 +49,7 @@ void UploadAssetTask::run() { auto replyPacket = NLPacket::create(PacketType::AssetUploadReply, -1, true); replyPacket->writePrimitive(messageID); - if (fileSize > MAX_UPLOAD_SIZE) { + if (fileSize > _filesizeLimit) { replyPacket->writePrimitive(AssetServerError::AssetTooLarge); } else { QByteArray fileData = buffer.read(fileSize); diff --git a/assignment-client/src/assets/UploadAssetTask.h b/assignment-client/src/assets/UploadAssetTask.h index 700eecbf9a..8c9e0d234a 100644 --- a/assignment-client/src/assets/UploadAssetTask.h +++ b/assignment-client/src/assets/UploadAssetTask.h @@ -26,7 +26,8 @@ class Node; class UploadAssetTask : public QRunnable { public: - UploadAssetTask(QSharedPointer message, QSharedPointer senderNode, const QDir& resourcesDir); + UploadAssetTask(QSharedPointer message, QSharedPointer senderNode, + const QDir& resourcesDir, uint64_t filesizeLimit); void run() override; @@ -34,6 +35,7 @@ private: QSharedPointer _receivedMessage; QSharedPointer _senderNode; QDir _resourcesDir; + uint64_t _filesizeLimit; }; #endif // hifi_UploadAssetTask_h diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 8d0e949ff3..19f1718370 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -858,6 +858,14 @@ "help": "The path to the directory assets are stored in.
If this path is relative, it will be relative to the application data directory.
If you change this path you will need to manually copy any existing assets from the previous directory.", "default": "", "advanced": true + }, + { + "name": "assets_filesize_limit", + "type": "int", + "label": "File Size Limit", + "help": "The file size limit of an asset that can be imported into the asset server in MBytes. 0 (default) means no limit on file size.", + "default": 0, + "advanced": true } ] }, diff --git a/interface/resources/meshes/defaultAvatar_full.fst b/interface/resources/meshes/defaultAvatar_full.fst index eb8e356196..aa1c17fc40 100644 --- a/interface/resources/meshes/defaultAvatar_full.fst +++ b/interface/resources/meshes/defaultAvatar_full.fst @@ -2,14 +2,14 @@ name = mannequin type = body+head scale = 1 filename = mannequin/mannequin.baked.fbx -joint = jointEyeLeft = LeftEye -joint = jointRightHand = RightHand -joint = jointHead = Head -joint = jointEyeRight = RightEye -joint = jointLean = Spine -joint = jointNeck = Neck -joint = jointLeftHand = LeftHand joint = jointRoot = Hips +joint = jointLean = Spine +joint = jointLeftHand = LeftHand +joint = jointHead = Head +joint = jointEyeLeft = LeftEye +joint = jointEyeRight = RightEye +joint = jointRightHand = RightHand +joint = jointNeck = Neck freeJoint = LeftArm freeJoint = LeftForeArm freeJoint = RightArm @@ -18,72 +18,72 @@ bs = EyeBlink_L = blink = 1 bs = JawOpen = mouth_Open = 1 bs = LipsFunnel = Oo = 1 bs = BrowsU_L = brow_Up = 1 -jointIndex = RightHandIndex2 = 27 -jointIndex = LeftHandIndex2 = 51 -jointIndex = RightUpLeg = 6 -jointIndex = RightToe_End = 10 -jointIndex = RightEye = 65 -jointIndex = LeftHandPinky1 = 42 -jointIndex = RightHandRing1 = 22 -jointIndex = face = 67 -jointIndex = LeftUpLeg = 1 -jointIndex = LeftHand = 41 -jointIndex = LeftHandMiddle1 = 58 -jointIndex = LeftHandIndex1 = 50 -jointIndex = LeftEye = 64 -jointIndex = RightHandIndex1 = 26 -jointIndex = LeftHandPinky4 = 45 -jointIndex = RightArm = 15 -jointIndex = LeftShoulder = 38 jointIndex = RightHandPinky2 = 19 -jointIndex = RightHandThumb1 = 30 -jointIndex = RightForeArm = 16 -jointIndex = LeftHandMiddle3 = 60 -jointIndex = Neck = 62 -jointIndex = LeftHandThumb1 = 54 -jointIndex = RightHandMiddle2 = 35 jointIndex = LeftHandMiddle4 = 61 -jointIndex = mannequin = 68 -jointIndex = Spine1 = 12 +jointIndex = LeftHand = 41 +jointIndex = LeftHandRing4 = 49 +jointIndex = RightHandMiddle3 = 36 +jointIndex = LeftHandThumb4 = 57 +jointIndex = RightToe_End = 10 +jointIndex = LeftHandRing1 = 46 +jointIndex = LeftForeArm = 40 +jointIndex = RightHandIndex4 = 29 +jointIndex = LeftShoulder = 38 +jointIndex = RightHandMiddle4 = 37 +jointIndex = RightShoulder = 14 +jointIndex = LeftLeg = 2 +jointIndex = LeftToe_End = 5 +jointIndex = Hips = 0 jointIndex = RightFoot = 8 +jointIndex = RightHandThumb2 = 31 +jointIndex = LeftHandMiddle3 = 60 +jointIndex = RightHandThumb1 = 30 +jointIndex = Neck = 62 +jointIndex = Spine = 11 +jointIndex = RightHandThumb4 = 33 +jointIndex = RightHandMiddle1 = 34 +jointIndex = LeftHandIndex4 = 53 +jointIndex = face = 68 +jointIndex = RightHandRing3 = 24 +jointIndex = LeftHandPinky4 = 45 +jointIndex = LeftHandMiddle2 = 59 +jointIndex = RightHandThumb3 = 32 +jointIndex = LeftHandPinky3 = 44 +jointIndex = HeadTop_End = 66 +jointIndex = Spine1 = 12 +jointIndex = LeftHandRing3 = 48 +jointIndex = mannequin1 = 67 +jointIndex = RightEye = 65 +jointIndex = RightHandRing4 = 25 +jointIndex = RightHandPinky4 = 21 +jointIndex = LeftHandRing2 = 47 +jointIndex = RightHandIndex3 = 28 +jointIndex = RightUpLeg = 6 +jointIndex = LeftArm = 39 +jointIndex = LeftHandThumb3 = 56 +jointIndex = RightHandIndex2 = 27 +jointIndex = RightForeArm = 16 +jointIndex = RightArm = 15 +jointIndex = RightHandRing2 = 23 +jointIndex = LeftHandMiddle1 = 58 +jointIndex = Spine2 = 13 +jointIndex = LeftHandThumb2 = 55 +jointIndex = RightHandMiddle2 = 35 +jointIndex = RightHandPinky1 = 18 +jointIndex = LeftUpLeg = 1 +jointIndex = RightLeg = 7 +jointIndex = LeftHandIndex2 = 51 jointIndex = RightHand = 17 jointIndex = LeftHandIndex3 = 52 -jointIndex = RightHandIndex3 = 28 -jointIndex = RightHandMiddle4 = 37 -jointIndex = LeftLeg = 2 -jointIndex = RightHandMiddle1 = 34 -jointIndex = Spine2 = 13 -jointIndex = LeftHandMiddle2 = 59 -jointIndex = LeftHandPinky3 = 44 -jointIndex = LeftHandThumb3 = 56 -jointIndex = LeftHandRing4 = 49 -jointIndex = RightHandThumb2 = 31 -jointIndex = LeftHandRing3 = 48 -jointIndex = HeadTop_End = 66 -jointIndex = LeftHandThumb4 = 57 -jointIndex = RightHandThumb3 = 32 -jointIndex = RightHandPinky1 = 18 -jointIndex = RightLeg = 7 -jointIndex = RightHandMiddle3 = 36 -jointIndex = RightHandPinky3 = 20 -jointIndex = LeftToeBase = 4 -jointIndex = LeftForeArm = 40 -jointIndex = RightShoulder = 14 -jointIndex = LeftHandRing2 = 47 -jointIndex = LeftHandThumb2 = 55 -jointIndex = Head = 63 -jointIndex = RightHandRing4 = 25 -jointIndex = LeftHandRing1 = 46 jointIndex = LeftFoot = 3 -jointIndex = RightHandRing3 = 24 -jointIndex = RightHandThumb4 = 33 -jointIndex = LeftArm = 39 -jointIndex = LeftToe_End = 5 +jointIndex = RightHandPinky3 = 20 +jointIndex = RightHandIndex1 = 26 +jointIndex = LeftHandPinky1 = 42 jointIndex = RightToeBase = 9 -jointIndex = RightHandPinky4 = 21 -jointIndex = Spine = 11 -jointIndex = LeftHandIndex4 = 53 +jointIndex = LeftHandIndex1 = 50 +jointIndex = LeftToeBase = 4 jointIndex = LeftHandPinky2 = 43 -jointIndex = RightHandIndex4 = 29 -jointIndex = Hips = 0 -jointIndex = RightHandRing2 = 23 +jointIndex = RightHandRing1 = 22 +jointIndex = LeftHandThumb1 = 54 +jointIndex = LeftEye = 64 +jointIndex = Head = 63 diff --git a/interface/resources/meshes/mannequin/Eyes.ktx b/interface/resources/meshes/mannequin/Eyes.ktx old mode 100755 new mode 100644 index 4da922936b..ada45776a0 Binary files a/interface/resources/meshes/mannequin/Eyes.ktx and b/interface/resources/meshes/mannequin/Eyes.ktx differ diff --git a/interface/resources/meshes/mannequin/lambert1_Base_Color.ktx b/interface/resources/meshes/mannequin/lambert1_Base_Color.ktx old mode 100755 new mode 100644 index fcca382445..f151352592 Binary files a/interface/resources/meshes/mannequin/lambert1_Base_Color.ktx and b/interface/resources/meshes/mannequin/lambert1_Base_Color.ktx differ diff --git a/interface/resources/meshes/mannequin/lambert1_Normal_OpenGL.ktx b/interface/resources/meshes/mannequin/lambert1_Normal_OpenGL.ktx old mode 100755 new mode 100644 index 3ae717c5d7..fb738063ae Binary files a/interface/resources/meshes/mannequin/lambert1_Normal_OpenGL.ktx and b/interface/resources/meshes/mannequin/lambert1_Normal_OpenGL.ktx differ diff --git a/interface/resources/meshes/mannequin/lambert1_Roughness.ktx b/interface/resources/meshes/mannequin/lambert1_Roughness.ktx old mode 100755 new mode 100644 index fe9b42a54b..afe96d1631 Binary files a/interface/resources/meshes/mannequin/lambert1_Roughness.ktx and b/interface/resources/meshes/mannequin/lambert1_Roughness.ktx differ diff --git a/interface/resources/meshes/mannequin/mannequin.baked.fbx b/interface/resources/meshes/mannequin/mannequin.baked.fbx old mode 100755 new mode 100644 index b405b4cfbb..2b0e1cf04b Binary files a/interface/resources/meshes/mannequin/mannequin.baked.fbx and b/interface/resources/meshes/mannequin/mannequin.baked.fbx differ diff --git a/interface/resources/qml/AvatarInputs.qml b/interface/resources/qml/AvatarInputs.qml index d86d4fd022..be4bf03465 100644 --- a/interface/resources/qml/AvatarInputs.qml +++ b/interface/resources/qml/AvatarInputs.qml @@ -17,6 +17,7 @@ import "./hifi/audio" as HifiAudio Hifi.AvatarInputs { id: root; objectName: "AvatarInputs" + property int modality: Qt.NonModal width: audio.width; height: audio.height; x: 10; y: 5; diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 8737e422cb..4626d9bcda 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -8,6 +8,9 @@ Item { anchors.leftMargin: 300 objectName: "StatsItem" + property int modality: Qt.NonModal + implicitHeight: row.height + implicitWidth: row.width Component.onCompleted: { stats.parentChanged.connect(fill); @@ -18,8 +21,9 @@ Item { } function fill() { - // Explicitly fill in order to avoid warnings at shutdown - anchors.fill = parent; + // This will cause a warning at shutdown, need to find another way to remove + // the warning other than filling the anchors to the parent + anchors.horizontalCenter = parent.horizontalCenter } Hifi.Stats { diff --git a/interface/resources/qml/desktop/Desktop.qml b/interface/resources/qml/desktop/Desktop.qml index bb23148ec6..847111b8a0 100644 --- a/interface/resources/qml/desktop/Desktop.qml +++ b/interface/resources/qml/desktop/Desktop.qml @@ -301,15 +301,19 @@ FocusScope { function isPointOnWindow(point) { for (var i = 0; i < desktop.visibleChildren.length; i++) { var child = desktop.visibleChildren[i]; - if (child.visible) { - if (child.hasOwnProperty("modality")) { - var mappedPoint = child.mapFromGlobal(point.x, point.y); + if (child.hasOwnProperty("modality")) { + var mappedPoint = mapToItem(child, point.x, point.y); + if (child.hasOwnProperty("frame")) { var outLine = child.frame.children[2]; var framePoint = outLine.mapFromGlobal(point.x, point.y); - if (child.contains(mappedPoint) || outLine.contains(framePoint)) { + if (outLine.contains(framePoint)) { return true; } } + + if (child.contains(mappedPoint)) { + return true; + } } } return false; diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h index 2eefe6ea22..e5040b1f90 100644 --- a/interface/src/scripting/HMDScriptingInterface.h +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -32,6 +32,7 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen Q_PROPERTY(bool showTablet READ getShouldShowTablet) Q_PROPERTY(QUuid tabletID READ getCurrentTabletFrameID WRITE setCurrentTabletFrameID) Q_PROPERTY(QUuid homeButtonID READ getCurrentHomeButtonID WRITE setCurrentHomeButtonID) + Q_PROPERTY(QUuid homeButtonHighlightID READ getCurrentHomeButtonHightlightID WRITE setCurrentHomeButtonHightlightID) Q_PROPERTY(QUuid tabletScreenID READ getCurrentTabletScreenID WRITE setCurrentTabletScreenID) public: @@ -97,6 +98,9 @@ public: void setCurrentHomeButtonID(QUuid homeButtonID) { _homeButtonID = homeButtonID; } QUuid getCurrentHomeButtonID() const { return _homeButtonID; } + void setCurrentHomeButtonHightlightID(QUuid homeButtonHightlightID) { _homeButtonHightlightID = homeButtonHightlightID; } + QUuid getCurrentHomeButtonHightlightID() const { return _homeButtonHightlightID; } + void setCurrentTabletScreenID(QUuid tabletID) { _tabletScreenID = tabletID; } QUuid getCurrentTabletScreenID() const { return _tabletScreenID; } @@ -105,6 +109,7 @@ private: QUuid _tabletUIID; // this is the entityID of the tablet frame QUuid _tabletScreenID; // this is the overlayID which is part of (a child of) the tablet-ui. QUuid _homeButtonID; + QUuid _homeButtonHightlightID; QUuid _tabletEntityID; // Get the position of the HMD diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 8140dfb8ae..d4a300c337 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -106,14 +106,15 @@ extern std::atomic DECIMATED_TEXTURE_COUNT; extern std::atomic RECTIFIED_TEXTURE_COUNT; void Stats::updateStats(bool force) { + QQuickItem* parent = parentItem(); if (!force) { if (!Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { - if (isVisible()) { - setVisible(false); + if (parent->isVisible()) { + parent->setVisible(false); } return; - } else if (!isVisible()) { - setVisible(true); + } else if (!parent->isVisible()) { + parent->setVisible(true); } } diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 5d8f840c85..4184351c2d 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -620,6 +620,12 @@ void NetworkTexture::ktxMipRequestFinished() { texture->assignStoredMip(mipLevel, data.size(), reinterpret_cast(data.data())); + // If mip level assigned above is still unavailable, then we assume future requests will also fail. + auto minMipLevel = texture->minAvailableMipLevel(); + if (minMipLevel > mipLevel) { + return; + } + QMetaObject::invokeMethod(resource.data(), "setImage", Q_ARG(gpu::TexturePointer, texture), Q_ARG(int, texture->getWidth()), diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 2f3f65c90b..7c21652ba2 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -37,7 +37,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AvatarData: case PacketType::BulkAvatarData: case PacketType::KillAvatar: - return static_cast(AvatarMixerPacketVersion::AvatarIdentityLookAtSnapping); + return static_cast(AvatarMixerPacketVersion::UpdatedMannequinDefaultAvatar); case PacketType::MessagesData: return static_cast(MessageDataVersion::TextOrBinaryData); case PacketType::ICEServerHeartbeat: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 122be60870..e514c27f7d 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -300,6 +300,7 @@ enum class AvatarMixerPacketVersion : PacketVersion { AvatarIdentitySequenceFront, IsReplicatedInAvatarIdentity, AvatarIdentityLookAtSnapping, + UpdatedMannequinDefaultAvatar }; enum class DomainConnectRequestVersion : PacketVersion { diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index 4978f225ce..ef7a7ab062 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -546,6 +546,12 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(this.targetEntityID, "releaseEquip", args); + Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ + action: 'release', + grabbedEntity: this.targetEntityID, + joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" + })); + ensureDynamic(this.targetEntityID); this.targetEntityID = null; this.messageGrabEntity = false; diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js index 2484067655..12d1533703 100644 --- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -144,6 +144,12 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); Entities.deleteAction(this.targetEntityID, this.actionID); this.actionID = null; + Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ + action: 'release', + grabbedEntity: this.targetEntityID, + joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" + })); + this.targetEntityID = null; }; diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index 70d91bf1ec..87d4811967 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -138,6 +138,11 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(this.targetEntityID, "releaseGrab", args); + Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ + action: 'release', + grabbedEntity: this.targetEntityID, + joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" + })); this.grabbing = false; this.targetEntityID = null; }; diff --git a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js index f9557f685f..471ee89926 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js @@ -148,6 +148,12 @@ Script.include("/~/system/libraries/utils.js"); } } + Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ + action: 'release', + grabbedEntity: this.grabbedThingID, + joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand" + })); + this.grabbedThingID = null; }; diff --git a/scripts/system/controllers/controllerModules/tabletStylusInput.js b/scripts/system/controllers/controllerModules/tabletStylusInput.js index 29fa878cb1..b633c6e058 100644 --- a/scripts/system/controllers/controllerModules/tabletStylusInput.js +++ b/scripts/system/controllers/controllerModules/tabletStylusInput.js @@ -264,6 +264,8 @@ Script.include("/~/system/libraries/controllers.js"); if (!this.stylusTip.valid || this.overlayLaserActive(controllerData) || this.otherModuleNeedsToRun(controllerData)) { this.pointFinger(false); this.hideStylus(); + this.stylusTouchingTarget = false; + this.relinquishTouchFocus(); return false; } diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 92a5857390..3fa3283ff1 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -150,6 +150,23 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { parentJointIndex: -1 }); + this.homeButtonHighlightID = Overlays.addOverlay("circle3d", { + name: "homeButtonHighlight", + localPosition: { x: 0, y: -HOME_BUTTON_Y_OFFSET + 0.003, z: -0.0158 }, + localRotation: { x: 0, y: 1, z: 0, w: 0 }, + dimensions: { x: 4 * tabletScaleFactor, y: 4 * tabletScaleFactor, z: 4 * tabletScaleFactor }, + solid: true, + outerRadius: 25 * tabletScaleFactor, + innerRadius: 20 * tabletScaleFactor, + ignoreIntersection: true, + alpha: 1.0, + color: { red: 255, green: 255, blue: 255 }, + visible: visible, + drawInFront: false, + parentID: this.tabletEntityID, + parentJointIndex: -1 + }); + this.receive = function (channel, senderID, senderUUID, localOnly) { if (_this.homeButtonID == senderID) { var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); @@ -170,6 +187,23 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { } }; + this.myOnHoverEnterOverlay = function (overlayID, pointerEvent) { + _this.onHoverEnterOverlay(overlayID, pointerEvent); + }; + + Overlays.hoverEnterOverlay.connect(this.myOnHoverEnterOverlay); + + this.myOnHoverLeaveOverlay = function (overlayID, pointerEvent) { + _this.onHoverLeaveOverlay(overlayID, pointerEvent); + }; + + Overlays.hoverLeaveOverlay.connect(this.myOnHoverLeaveOverlay); + + this.myOnHoverOverOverlay = function (overlayID, pointerEvent) { + _this.onHoverOverOverlay(overlayID, pointerEvent); + }; + Overlays.hoverOverOverlay.connect(this.myOnHoverOverOverlay); + this.state = "idle"; this.getRoot = function() { @@ -273,9 +307,14 @@ WebTablet.prototype.setWidth = function (width) { }; WebTablet.prototype.destroy = function () { + Overlays.hoverEnterOverlay.disconnect(this.myOnHoverEnterOverlay); + Overlays.hoverLeaveOverlay.disconnect(this.myOnHoverLeaveOverlay); + Overlays.hoverOverOverlay.disconnect(this.myOnHoverOverOverlay); + Overlays.deleteOverlay(this.webOverlayID); Overlays.deleteOverlay(this.tabletEntityID); Overlays.deleteOverlay(this.homeButtonID); + Overlays.deleteOverlay(this.homeButtonHighlightID); HMD.displayModeChanged.disconnect(this.myOnHmdChanged); Controller.mousePressEvent.disconnect(this.myMousePressEvent); @@ -367,6 +406,24 @@ WebTablet.prototype.calculateWorldAttitudeRelativeToCamera = function (windowPos }; }; +WebTablet.prototype.onHoverEnterOverlay = function (overlayID, pointerEvent) { + if (overlayID === this.homeButtonID) { + Overlays.editOverlay(this.homeButtonHighlightID, { alpha: 1.0 }); + } +} + +WebTablet.prototype.onHoverOverOverlay = function (overlayID, pointerEvent) { + if (overlayID !== this.homeButtonID) { + Overlays.editOverlay(this.homeButtonHighlightID, { alpha: 0.0 }); + } +} + +WebTablet.prototype.onHoverLeaveOverlay = function (overlayID, pointerEvent) { + if (overlayID === this.homeButtonID) { + Overlays.editOverlay(this.homeButtonHighlightID, { alpha: 0.0 }); + } +} + // compute position, rotation & parentJointIndex of the tablet WebTablet.prototype.calculateTabletAttachmentProperties = function (hand, useMouse, tabletProperties) { if (HMD.active) { @@ -504,6 +561,22 @@ WebTablet.prototype.scheduleMouseMoveProcessor = function() { } }; +WebTablet.prototype.handleHomeButtonHover = function(x, y) { + var pickRay = Camera.computePickRay(x, y); + var entityPickResults; + var homebuttonHovered = false; + entityPickResults = Overlays.findRayIntersection(pickRay, true, [this.tabletEntityID]); + if (entityPickResults.intersects && (entityPickResults.entityID === this.tabletEntityID || + entityPickResults.overlayID === this.tabletEntityID)) { + var overlayPickResults = Overlays.findRayIntersection(pickRay, true, [this.homeButtonID], []); + if (overlayPickResults.intersects && overlayPickResults.overlayID === this.homeButtonID) { + homebuttonHovered = true; + } + } + + Overlays.editOverlay(this.homeButtonHighlightID, { alpha: homebuttonHovered ? 1.0 : 0.0 }); +} + WebTablet.prototype.mouseMoveEvent = function (event) { if (this.dragging) { this.currentMouse = { @@ -511,6 +584,8 @@ WebTablet.prototype.mouseMoveEvent = function (event) { y: event.y }; this.scheduleMouseMoveProcessor(); + } else { + this.handleHomeButtonHover(event.x, event.y); } }; @@ -537,6 +612,8 @@ WebTablet.prototype.mouseMoveProcessor = function () { }); } this.scheduleMouseMoveProcessor(); + } else { + this.handleHomeButtonHover(this.currentMouse.x, this.currentMouse.y); } }; diff --git a/scripts/system/libraries/utils.js b/scripts/system/libraries/utils.js index e6730b8826..76c248d880 100644 --- a/scripts/system/libraries/utils.js +++ b/scripts/system/libraries/utils.js @@ -370,7 +370,7 @@ getTabletWidthFromSettings = function () { resizeTablet = function (width, newParentJointIndex, sensorToWorldScaleOverride) { - if (!HMD.tabletID || !HMD.tabletScreenID || !HMD.homeButtonID) { + if (!HMD.tabletID || !HMD.tabletScreenID || !HMD.homeButtonID || !HMD.homeButtonHighlightID) { return; } @@ -413,4 +413,11 @@ resizeTablet = function (width, newParentJointIndex, sensorToWorldScaleOverride) localPosition: {x: -0.001, y: -HOME_BUTTON_Y_OFFSET, z: 0.0}, dimensions: { x: 4 * tabletScaleFactor, y: 4 * tabletScaleFactor, z: 4 * tabletScaleFactor} }); + + Overlays.editOverlay(HMD.homeButtonHighlightID, { + localPosition: { x: 0, y: -HOME_BUTTON_Y_OFFSET + 0.003, z: -0.0158 }, + dimensions: { x: 4 * tabletScaleFactor, y: 4 * tabletScaleFactor, z: 4 * tabletScaleFactor }, + outerRadius: 25 * tabletScaleFactor, + innerRadius: 20 * tabletScaleFactor + }); }; diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index 78b14eb7c6..554729f61e 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -103,6 +103,7 @@ UIWebTablet.register(); HMD.tabletID = UIWebTablet.tabletEntityID; HMD.homeButtonID = UIWebTablet.homeButtonID; + HMD.homeButtonHighlightID = UIWebTablet.homeButtonHighlightID; HMD.tabletScreenID = UIWebTablet.webOverlayID; HMD.displayModeChanged.connect(onHmdChanged); MyAvatar.sensorToWorldScaleChanged.connect(onSensorToWorldScaleChanged); @@ -127,6 +128,7 @@ tabletProperties.visible = true; Overlays.editOverlay(HMD.tabletID, tabletProperties); Overlays.editOverlay(HMD.homeButtonID, { visible: true }); + Overlays.editOverlay(HMD.homeButtonHighlightID, { visible: true }); Overlays.editOverlay(HMD.tabletScreenID, { visible: true }); Overlays.editOverlay(HMD.tabletScreenID, { maxFPS: 90 }); updateTabletWidthFromSettings(true); @@ -147,6 +149,7 @@ Overlays.editOverlay(HMD.tabletID, { visible: false }); Overlays.editOverlay(HMD.homeButtonID, { visible: false }); + Overlays.editOverlay(HMD.homeButtonHighlightID, { visible: false }); Overlays.editOverlay(HMD.tabletScreenID, { visible: false }); Overlays.editOverlay(HMD.tabletScreenID, { maxFPS: 1 }); } @@ -167,6 +170,7 @@ UIWebTablet = null; HMD.tabletID = null; HMD.homeButtonID = null; + HMD.homeButtonHighlightID = null; HMD.tabletScreenID = null; } else if (debugTablet) { print("TABLET closeTabletUI, UIWebTablet is null"); @@ -319,6 +323,7 @@ Overlays.deleteOverlay(tabletID); HMD.tabletID = null; HMD.homeButtonID = null; + HMD.homeButtonHighlightID = null; HMD.tabletScreenID = null; }); }()); // END LOCAL_SCOPE diff --git a/unpublishedScripts/marketplace/shapes/modules/createPalette.js b/unpublishedScripts/marketplace/shapes/modules/createPalette.js index 0eea8379d6..38dad5b827 100644 --- a/unpublishedScripts/marketplace/shapes/modules/createPalette.js +++ b/unpublishedScripts/marketplace/shapes/modules/createPalette.js @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* global CreatePalette: true, App, Feedback, History, UIT */ +/* global CreatePalette:true, App, Feedback, History, Preload, UIT */ CreatePalette = function (side, leftInputs, rightInputs, uiCommandCallback) { // Tool menu displayed on top of forearm. @@ -332,6 +332,11 @@ CreatePalette = function (side, leftInputs, rightInputs, uiCommandCallback) { } + function getAssetURLs() { + return Preload.findURLs([PALETTE_HEADER_HEADING_PROPERTIES, PALETTE_HEADER_BAR_PROPERTIES, PALETTE_TITLE_PROPERTIES, + PALETTE_ITEMS]); + } + function setHand(hand) { // Assumes UI is not displaying. var NUMBER_OF_HANDS = 2; @@ -533,6 +538,7 @@ CreatePalette = function (side, leftInputs, rightInputs, uiCommandCallback) { } return { + assetURLs: getAssetURLs, setHand: setHand, overlayIDs: getOverlayIDs, setVisible: setVisible, diff --git a/unpublishedScripts/marketplace/shapes/modules/preload.js b/unpublishedScripts/marketplace/shapes/modules/preload.js new file mode 100644 index 0000000000..e93ef9ac71 --- /dev/null +++ b/unpublishedScripts/marketplace/shapes/modules/preload.js @@ -0,0 +1,175 @@ +// +// preload.js +// +// Created by David Rowe on 11 Oct 2017. +// Copyright 2017 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 +// + +/* global Preload:true, App */ + +Preload = (function () { + // Provide facility to preload asset files so that they are in disk cache. + // Global object. + + "use strict"; + + var loadTimer = null, + urlsToLoad = [], + nextURLToLoad = 0, + LOAD_INTERVAL = 50, // ms + overlays = [], + deleteTimer = null, + DELETE_INTERVAL = LOAD_INTERVAL; // ms + + function findURLs(items) { + var urls = [], + i, + length; + + function findURLsInObject(item) { + var property; + + for (property in item) { + if (item.hasOwnProperty(property)) { + if (property === "url" || property === "imageURL" || property === "imageOverlayURL") { + if (item[property]) { + urls.push(item[property]); + } + } else if (typeof item[property] === "object") { + findURLsInObject(item[property]); + } + } + } + } + + for (i = 0, length = items.length; i < length; i++) { + switch (typeof items[i]) { + case "string": + urls.push(items[i]); + break; + case "object": + findURLsInObject(items[i]); + break; + default: + App.log("ERROR: Cannot find URL in item type " + (typeof items[i])); + } + } + + return urls; + } + + function deleteOverlay() { + if (overlays.length === 0) { // Just in case. + deleteTimer = null; + return; + } + + Overlays.deleteOverlay(overlays[0]); + overlays.shift(); + + if (overlays.length > 0) { + deleteTimer = Script.setTimeout(deleteOverlay, DELETE_INTERVAL); + } else { + deleteTimer = null; + } + } + + function loadNextURL() { + + function loadURL(url) { + var DOMAIN_CORNER = { x: -16382, y: -16382, z: -16382 }, // Near but not right on domain corner. + DUMMY_OVERLAY_PROPERTIES = { + fbx: { + overlay: "model", + dimensions: { x: 0.001, y: 0.001, z: 0.001 }, + position: DOMAIN_CORNER, + ignoreRayIntersection: false, + alpha: 0.0, + visible: false + }, + svg: { + overlay: "image3d", + scale: 0.001, + position: DOMAIN_CORNER, + ignoreRayIntersection: true, + alpha: 0.0, + visible: false + }, + png: { + overlay: "image3d", + scale: 0.001, + position: DOMAIN_CORNER, + ignoreRayIntersection: true, + alpha: 0.0, + visible: false + } + }, + fileType, + properties; + + fileType = url.slice(-3); + if (DUMMY_OVERLAY_PROPERTIES.hasOwnProperty(fileType)) { + properties = Object.clone(DUMMY_OVERLAY_PROPERTIES[fileType]); + properties.url = url; + overlays.push(Overlays.addOverlay(properties.overlay, properties)); + if (deleteTimer === null) { + // Can't delete overlay straight away otherwise asset load is abandoned. + deleteTimer = Script.setTimeout(deleteOverlay, DELETE_INTERVAL); + } + } else { + App.log("ERROR: Cannot preload asset " + url); + } + } + + // Find next URL that hasn't already been loaded; + while (nextURLToLoad < urlsToLoad.length && urlsToLoad.indexOf(urlsToLoad[nextURLToLoad]) < nextURLToLoad) { + nextURLToLoad += 1; + } + + // Load the URL if there's one to load. + if (nextURLToLoad < urlsToLoad.length) { + // Load the URL. + loadURL(urlsToLoad[nextURLToLoad]); + nextURLToLoad += 1; + + // Load the next, if any, after a short delay. + loadTimer = Script.setTimeout(loadNextURL, LOAD_INTERVAL); + } else { + loadTimer = null; + } + } + + function load(urls) { + urlsToLoad = urlsToLoad.concat(urls); + if (loadTimer === null) { + loadNextURL(); + } + } + + function tearDown() { + var i, + length; + + if (loadTimer) { + Script.clearTimeout(loadTimer); + } + + if (deleteTimer) { + Script.clearTimeout(deleteTimer); + for (i = 0, length = overlays.length; i < length; i++) { + Overlays.deleteOverlay(overlays[i]); + } + } + } + + Script.scriptEnding.connect(tearDown); + + return { + findURLs: findURLs, + load: load + }; + +}()); diff --git a/unpublishedScripts/marketplace/shapes/modules/toolIcon.js b/unpublishedScripts/marketplace/shapes/modules/toolIcon.js index 1571a6b037..143d768466 100644 --- a/unpublishedScripts/marketplace/shapes/modules/toolIcon.js +++ b/unpublishedScripts/marketplace/shapes/modules/toolIcon.js @@ -72,6 +72,10 @@ ToolIcon = function (side) { return new ToolIcon(); } + function getAssetURLs() { + return [MODEL_PROPERTIES.url]; + } + function setHand(side) { // Assumes UI is not displaying. if (side === LEFT_HAND) { @@ -154,6 +158,7 @@ ToolIcon = function (side) { } return { + assetURLs: getAssetURLs, setHand: setHand, display: display, clear: clear, diff --git a/unpublishedScripts/marketplace/shapes/modules/toolsMenu.js b/unpublishedScripts/marketplace/shapes/modules/toolsMenu.js index 19c114c8e9..04bb88d040 100644 --- a/unpublishedScripts/marketplace/shapes/modules/toolsMenu.js +++ b/unpublishedScripts/marketplace/shapes/modules/toolsMenu.js @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* global ToolsMenu: true, App, Feedback, UIT */ +/* global ToolsMenu:true, App, Feedback, Preload, UIT */ ToolsMenu = function (side, leftInputs, rightInputs, uiCommandCallback) { // Tool menu displayed on top of forearm. @@ -2042,6 +2042,12 @@ ToolsMenu = function (side, leftInputs, rightInputs, uiCommandCallback) { return new ToolsMenu(); } + function getAssetURLs() { + return Preload.findURLs([MENU_HEADER_HEADING_PROPERTIES, MENU_HEADER_BAR_PROPERTIES, MENU_HEADER_BACK_PROPERTIES, + MENU_HEADER_TITLE_PROPERTIES, MENU_HEADER_TITLE_BACK_URL, MENU_HEADER_ICON_PROPERTIES, UI_ELEMENTS, + PICKLIST_UP_ARROW, PICKLIST_DOWN_ARROW, OPTONS_PANELS, MENU_ITEMS, FOOTER_ITEMS]); + } + controlHand = side === LEFT_HAND ? rightInputs.hand() : leftInputs.hand(); function setHand(hand) { @@ -3672,6 +3678,7 @@ ToolsMenu = function (side, leftInputs, rightInputs, uiCommandCallback) { } return { + assetURLs: getAssetURLs, COLOR_TOOL: COLOR_TOOL, SCALE_TOOL: SCALE_TOOL, CLONE_TOOL: CLONE_TOOL, diff --git a/unpublishedScripts/marketplace/shapes/shapes.js b/unpublishedScripts/marketplace/shapes/shapes.js index 290424f55e..cd5f119588 100644 --- a/unpublishedScripts/marketplace/shapes/shapes.js +++ b/unpublishedScripts/marketplace/shapes/shapes.js @@ -8,7 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* global Feedback, History */ +/* global Feedback, History, Preload */ (function () { @@ -87,6 +87,7 @@ Script.include("./modules/highlights.js"); Script.include("./modules/history.js"); Script.include("./modules/laser.js"); + Script.include("./modules/preload.js"); Script.include("./modules/selection.js"); Script.include("./modules/toolIcon.js"); Script.include("./modules/toolsMenu.js"); @@ -235,8 +236,12 @@ } toolIcon = new ToolIcon(otherHand(side)); - toolsMenu = new ToolsMenu(side, leftInputs, rightInputs, uiCommandCallback); createPalette = new CreatePalette(side, leftInputs, rightInputs, uiCommandCallback); + toolsMenu = new ToolsMenu(side, leftInputs, rightInputs, uiCommandCallback); + + Preload.load(toolIcon.assetURLs()); + Preload.load(createPalette.assetURLs()); + Preload.load(toolsMenu.assetURLs()); getIntersection = side === LEFT_HAND ? rightInputs.intersection : leftInputs.intersection;