diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index c03721d097..e7d86c824e 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -49,10 +50,12 @@ static const int INTERFACE_RUNNING_CHECK_FREQUENCY_MS = 1000; const QString ASSET_SERVER_LOGGING_TARGET_NAME = "asset-server"; -static const QStringList BAKEABLE_MODEL_EXTENSIONS = { "fbx" }; +static const QStringList BAKEABLE_MODEL_EXTENSIONS = {"fbx"}; static QStringList BAKEABLE_TEXTURE_EXTENSIONS; +static const QStringList BAKEABLE_SCRIPT_EXTENSIONS = {"js"}; static const QString BAKED_MODEL_SIMPLE_NAME = "asset.fbx"; static const QString BAKED_TEXTURE_SIMPLE_NAME = "texture.ktx"; +static const QString BAKED_SCRIPT_SIMPLE_NAME = "asset.js"; void AssetServer::bakeAsset(const AssetHash& assetHash, const AssetPath& assetPath, const QString& filePath) { qDebug() << "Starting bake for: " << assetPath << assetHash; @@ -99,6 +102,8 @@ std::pair AssetServer::getAssetStatus(const AssetPath& pa bakedFilename = BAKED_MODEL_SIMPLE_NAME; } else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(extension.toLocal8Bit()) && hasMetaFile(hash)) { bakedFilename = BAKED_TEXTURE_SIMPLE_NAME; + } else if (BAKEABLE_SCRIPT_EXTENSIONS.contains(extension)) { + bakedFilename = BAKED_SCRIPT_SIMPLE_NAME; } else { return { Irrelevant, "" }; } @@ -186,6 +191,8 @@ bool AssetServer::needsToBeBaked(const AssetPath& path, const AssetHash& assetHa bakedFilename = BAKED_MODEL_SIMPLE_NAME; } else if (loaded && BAKEABLE_TEXTURE_EXTENSIONS.contains(extension.toLocal8Bit())) { bakedFilename = BAKED_TEXTURE_SIMPLE_NAME; + } else if (BAKEABLE_SCRIPT_EXTENSIONS.contains(extension)) { + bakedFilename = BAKED_SCRIPT_SIMPLE_NAME; } else { return false; } @@ -228,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(); @@ -336,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." @@ -399,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() { @@ -488,6 +505,8 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNode bakedRootFile = BAKED_MODEL_SIMPLE_NAME; } else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(assetPathExtension.toLocal8Bit())) { bakedRootFile = BAKED_TEXTURE_SIMPLE_NAME; + } else if (BAKEABLE_SCRIPT_EXTENSIONS.contains(assetPathExtension)) { + bakedRootFile = BAKED_SCRIPT_SIMPLE_NAME; } auto originalAssetHash = it->second; @@ -721,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 @@ -1141,6 +1160,7 @@ bool AssetServer::renameMapping(AssetPath oldPath, AssetPath newPath) { static const QString BAKED_ASSET_SIMPLE_FBX_NAME = "asset.fbx"; static const QString BAKED_ASSET_SIMPLE_TEXTURE_NAME = "texture.ktx"; +static const QString BAKED_ASSET_SIMPLE_JS_NAME = "asset.js"; QString getBakeMapping(const AssetHash& hash, const QString& relativeFilePath) { return HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + relativeFilePath; @@ -1204,14 +1224,14 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina // setup the mapping for this bake file auto relativeFilePath = QUrl(filePath).fileName(); qDebug() << "Relative file path is: " << relativeFilePath; - if (relativeFilePath.endsWith(".fbx", Qt::CaseInsensitive)) { // for an FBX file, we replace the filename with the simple name // (to handle the case where two mapped assets have the same hash but different names) relativeFilePath = BAKED_ASSET_SIMPLE_FBX_NAME; + } else if (relativeFilePath.endsWith(".js", Qt::CaseInsensitive)) { + relativeFilePath = BAKED_ASSET_SIMPLE_JS_NAME; } else if (!originalAssetPath.endsWith(".fbx", Qt::CaseInsensitive)) { relativeFilePath = BAKED_ASSET_SIMPLE_TEXTURE_NAME; - } QString bakeMapping = getBakeMapping(originalAssetHash, relativeFilePath); @@ -1364,6 +1384,8 @@ bool AssetServer::setBakingEnabled(const AssetPathList& paths, bool enabled) { bakedFilename = BAKED_MODEL_SIMPLE_NAME; } else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(extension.toLocal8Bit()) && hasMetaFile(hash)) { bakedFilename = BAKED_TEXTURE_SIMPLE_NAME; + } else if (BAKEABLE_SCRIPT_EXTENSIONS.contains(extension)) { + bakedFilename = BAKED_SCRIPT_SIMPLE_NAME; } else { continue; } 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/BakeAssetTask.cpp b/assignment-client/src/assets/BakeAssetTask.cpp index 94a0739612..6c78d2baf3 100644 --- a/assignment-client/src/assets/BakeAssetTask.cpp +++ b/assignment-client/src/assets/BakeAssetTask.cpp @@ -15,6 +15,7 @@ #include #include +#include BakeAssetTask::BakeAssetTask(const AssetHash& assetHash, const AssetPath& assetPath, const QString& filePath) : _assetHash(assetHash), @@ -52,6 +53,10 @@ void BakeAssetTask::run() { _baker = std::unique_ptr { new FBXBaker(QUrl("file:///" + _filePath), fn, tempOutputDir) }; + } else if (_assetPath.endsWith(".js", Qt::CaseInsensitive)) { + _baker = std::unique_ptr{ + new JSBaker(QUrl("file:///" + _filePath), PathUtils::generateTemporaryDir()) + }; } else { tempOutputDir = PathUtils::generateTemporaryDir(); _baker = std::unique_ptr { 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/controls-uit/Keyboard.qml b/interface/resources/qml/controls-uit/Keyboard.qml index 81579d9f71..8d6634c9b4 100644 --- a/interface/resources/qml/controls-uit/Keyboard.qml +++ b/interface/resources/qml/controls-uit/Keyboard.qml @@ -13,6 +13,7 @@ import "." Rectangle { id: keyboardBase + objectName: "keyboard" anchors.left: parent.left anchors.right: parent.right @@ -27,6 +28,8 @@ Rectangle { readonly property int mirrorTextHeight: keyboardRowHeight + property bool password: false + property alias mirroredText: mirrorText.text property bool showMirrorText: true readonly property int raisedHeight: 200 @@ -112,19 +115,23 @@ Rectangle { color: "#252525" anchors.horizontalCenter: parent.horizontalCenter - TextEdit { + TextInput { id: mirrorText visible: showMirrorText - size: 13.5 + FontLoader { id: ralewaySemiBold; source: "../../fonts/Raleway-SemiBold.ttf"; } + font.family: ralewaySemiBold.name + font.pointSize: 13.5 + verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter color: "#FFFFFF"; anchors.fill: parent wrapMode: Text.WordWrap - readOnly: false // we need to leave this property read-only to allow control to accept QKeyEvent + readOnly: false // we need this to allow control to accept QKeyEvent selectByMouse: false + echoMode: password ? TextInput.Password : TextInput.Normal Keys.onPressed: { - if (event.key == Qt.Key_Return) { + if (event.key == Qt.Key_Return || event.key == Qt.Key_Space) { mirrorText.text = ""; event.accepted = true; } diff --git a/interface/resources/qml/controls-uit/TextField.qml b/interface/resources/qml/controls-uit/TextField.qml index 1cc4d0ecc1..e636bfc27f 100644 --- a/interface/resources/qml/controls-uit/TextField.qml +++ b/interface/resources/qml/controls-uit/TextField.qml @@ -39,6 +39,20 @@ TextField { y: textFieldLabel.visible ? textFieldLabel.height + textFieldLabel.anchors.bottomMargin : 0 + // workaround for https://bugreports.qt.io/browse/QTBUG-49297 + Keys.onPressed: { + switch (event.key) { + case Qt.Key_Return: + case Qt.Key_Enter: + event.accepted = true; + + // emit accepted signal manually + if (acceptableInput) { + accepted(); + } + } + } + style: TextFieldStyle { textColor: { if (isLightColorScheme) { 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/Application.cpp b/interface/src/Application.cpp index 30390c007b..f9ad6ec7ff 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -79,8 +79,6 @@ #include #include #include -#include -#include #include #include #include @@ -209,6 +207,17 @@ #if defined(Q_OS_WIN) #include +#ifdef DEBUG_EVENT_QUEUE +// This is a HACK that uses private headers included with the qt source distrubution. +// To use this feature you need to add these directores to your include path: +// E:/Qt/5.9.1/Src/qtbase/include/QtCore/5.9.1/QtCore +// E:/Qt/5.9.1/Src/qtbase/include/QtCore/5.9.1 +#define QT_BOOTSTRAPPED +#include +#include +#undef QT_BOOTSTRAPPED +#endif + extern "C" { _declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; } @@ -266,9 +275,7 @@ private: switch ((int)event->type()) { case ApplicationEvent::Render: render(); - // Ensure we never back up the render events. Each render should be triggered only in response - // to the NEXT render event after the last render occured - QCoreApplication::removePostedEvents(this, ApplicationEvent::Render); + qApp->_pendingRenderEvent.store(false); return true; default: @@ -2714,9 +2721,14 @@ bool Application::importFromZIP(const QString& filePath) { return true; } +// thread-safe void Application::onPresent(quint32 frameCount) { - postEvent(this, new QEvent((QEvent::Type)ApplicationEvent::Idle), Qt::HighEventPriority); - if (_renderEventHandler && !isAboutToQuit()) { + bool expected = false; + if (_pendingIdleEvent.compare_exchange_strong(expected, true)) { + postEvent(this, new QEvent((QEvent::Type)ApplicationEvent::Idle), Qt::HighEventPriority); + } + expected = false; + if (_renderEventHandler && !isAboutToQuit() && _pendingRenderEvent.compare_exchange_strong(expected, true)) { postEvent(_renderEventHandler, new QEvent((QEvent::Type)ApplicationEvent::Render)); } } @@ -2783,7 +2795,26 @@ bool Application::handleFileOpenEvent(QFileOpenEvent* fileEvent) { return false; } +#ifdef DEBUG_EVENT_QUEUE +static int getEventQueueSize(QThread* thread) { + auto threadData = QThreadData::get2(thread); + QMutexLocker locker(&threadData->postEventList.mutex); + return threadData->postEventList.size(); +} + +static void dumpEventQueue(QThread* thread) { + auto threadData = QThreadData::get2(thread); + QMutexLocker locker(&threadData->postEventList.mutex); + qDebug() << "AJT: event list, size =" << threadData->postEventList.size(); + for (auto& postEvent : threadData->postEventList) { + QEvent::Type type = (postEvent.event ? postEvent.event->type() : QEvent::None); + qDebug() << "AJT: " << type; + } +} +#endif // DEBUG_EVENT_QUEUE + bool Application::event(QEvent* event) { + if (!Menu::getInstance()) { return false; } @@ -2803,8 +2834,18 @@ bool Application::event(QEvent* event) { // see (windowMinimizedChanged) case ApplicationEvent::Idle: idle(); - // Don't process extra idle events that arrived in the event queue while we were doing this idle - QCoreApplication::removePostedEvents(this, ApplicationEvent::Idle); + +#ifdef DEBUG_EVENT_QUEUE + { + int count = getEventQueueSize(QThread::currentThread()); + if (count > 400) { + dumpEventQueue(QThread::currentThread()); + } + } +#endif // DEBUG_EVENT_QUEUE + + _pendingIdleEvent.store(false); + return true; case QEvent::MouseMove: @@ -2858,10 +2899,6 @@ bool Application::event(QEvent* event) { break; } - if (HFActionEvent::types().contains(event->type())) { - _controllerScriptingInterface->handleMetaEvent(static_cast(event)); - } - return QApplication::event(event); } @@ -3166,25 +3203,8 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_Equal: getMyAvatar()->resetSize(); break; - case Qt::Key_Space: { - if (!event->isAutoRepeat()) { - // FIXME -- I don't think we've tested the HFActionEvent in a while... this looks possibly dubious - // this starts an HFActionEvent - HFActionEvent startActionEvent(HFActionEvent::startType(), - computePickRay(getMouse().x, getMouse().y)); - sendEvent(this, &startActionEvent); - } - - break; - } case Qt::Key_Escape: { getActiveDisplayPlugin()->abandonCalibration(); - if (!event->isAutoRepeat()) { - // this starts the HFCancelEvent - HFBackEvent startBackEvent(HFBackEvent::startType()); - sendEvent(this, &startBackEvent); - } - break; } @@ -3210,30 +3230,6 @@ void Application::keyReleaseEvent(QKeyEvent* event) { if (_keyboardMouseDevice->isActive()) { _keyboardMouseDevice->keyReleaseEvent(event); } - - switch (event->key()) { - case Qt::Key_Space: { - if (!event->isAutoRepeat()) { - // FIXME -- I don't think we've tested the HFActionEvent in a while... this looks possibly dubious - // this ends the HFActionEvent - HFActionEvent endActionEvent(HFActionEvent::endType(), - computePickRay(getMouse().x, getMouse().y)); - sendEvent(this, &endActionEvent); - } - break; - } - case Qt::Key_Escape: { - if (!event->isAutoRepeat()) { - // this ends the HFCancelEvent - HFBackEvent endBackEvent(HFBackEvent::endType()); - sendEvent(this, &endBackEvent); - } - break; - } - default: - event->ignore(); - break; - } } void Application::focusOutEvent(QFocusEvent* event) { @@ -3370,13 +3366,6 @@ void Application::mousePressEvent(QMouseEvent* event) { if (_keyboardMouseDevice->isActive()) { _keyboardMouseDevice->mousePressEvent(event); } - - if (event->button() == Qt::LeftButton) { - // nobody handled this - make it an action event on the _window object - HFActionEvent actionEvent(HFActionEvent::startType(), - computePickRay(mappedEvent.x(), mappedEvent.y())); - sendEvent(this, &actionEvent); - } } } @@ -3431,13 +3420,6 @@ void Application::mouseReleaseEvent(QMouseEvent* event) { if (_keyboardMouseDevice->isActive()) { _keyboardMouseDevice->mouseReleaseEvent(event); } - - if (event->button() == Qt::LeftButton) { - // fire an action end event - HFActionEvent actionEvent(HFActionEvent::endType(), - computePickRay(mappedEvent.x(), mappedEvent.y())); - sendEvent(this, &actionEvent); - } } } @@ -4024,9 +4006,14 @@ void Application::calibrateEyeTracker5Points() { } #endif -bool Application::exportEntities(const QString& filename, const QVector& entityIDs, const glm::vec3* givenOffset) { +bool Application::exportEntities(const QString& filename, + const QVector& entityIDs, + const glm::vec3* givenOffset) { QHash entities; + auto nodeList = DependencyManager::get(); + const QUuid myAvatarID = nodeList->getSessionUUID(); + auto entityTree = getEntities()->getTree(); auto exportTree = std::make_shared(); exportTree->createRootElement(); @@ -4042,8 +4029,12 @@ bool Application::exportEntities(const QString& filename, const QVectorgetParentID(); - if (parentID.isInvalidID() || !entityIDs.contains(parentID) || !entityTree->findEntityByEntityItemID(parentID)) { - auto position = entityItem->getPosition(); // If parent wasn't selected, we want absolute position, which isn't in properties. + bool parentIsAvatar = (parentID == AVATAR_SELF_ID || parentID == myAvatarID); + if (!parentIsAvatar && (parentID.isInvalidID() || + !entityIDs.contains(parentID) || + !entityTree->findEntityByEntityItemID(parentID))) { + // If parent wasn't selected, we want absolute position, which isn't in properties. + auto position = entityItem->getPosition(); root.x = glm::min(root.x, position.x); root.y = glm::min(root.y, position.y); root.z = glm::min(root.z, position.z); @@ -4063,12 +4054,16 @@ bool Application::exportEntities(const QString& filename, const QVectorgetProperties(); EntityItemID parentID = properties.getParentID(); - if (parentID.isInvalidID()) { - properties.setPosition(properties.getPosition() - root); + bool parentIsAvatar = (parentID == AVATAR_SELF_ID || parentID == myAvatarID); + if (parentIsAvatar) { + properties.setParentID(AVATAR_SELF_ID); + } else { + if (parentID.isInvalidID()) { + properties.setPosition(properties.getPosition() - root); + } else if (!entities.contains(parentID)) { + entityDatum->globalizeProperties(properties, "Parent %3 of %2 %1 is not selected for export.", -root); + } // else valid parent -- don't offset } - else if (!entities.contains(parentID)) { - entityDatum->globalizeProperties(properties, "Parent %3 of %2 %1 is not selected for export.", -root); - } // else valid parent -- don't offset exportTree->addEntity(entityDatum->getEntityItemID(), properties); } }); @@ -7251,7 +7246,7 @@ void Application::updateDisplayMode() { _offscreenContext->makeCurrent(); getApplicationCompositor().setDisplayPlugin(newDisplayPlugin); _displayPlugin = newDisplayPlugin; - connect(_displayPlugin.get(), &DisplayPlugin::presented, this, &Application::onPresent); + connect(_displayPlugin.get(), &DisplayPlugin::presented, this, &Application::onPresent, Qt::DirectConnection); auto desktop = offscreenUi->getDesktop(); if (desktop) { desktop->setProperty("repositionLocked", wasRepositionLocked); diff --git a/interface/src/Application.h b/interface/src/Application.h index 772646f379..b6c09bbd87 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -717,5 +717,8 @@ private: LaserPointerManager _laserPointerManager; friend class RenderEventHandler; + + std::atomic _pendingIdleEvent { false }; + std::atomic _pendingRenderEvent { false }; }; #endif // hifi_Application_h diff --git a/interface/src/Application_render.cpp b/interface/src/Application_render.cpp index 541197a660..44d9dfee03 100644 --- a/interface/src/Application_render.cpp +++ b/interface/src/Application_render.cpp @@ -72,6 +72,12 @@ void Application::paintGL() { { QMutexLocker viewLocker(&_renderArgsMutex); renderArgs = _appRenderArgs._renderArgs; + + // don't render if there is no context. + if (!_appRenderArgs._renderArgs._context) { + return; + } + HMDSensorPose = _appRenderArgs._headPose; eyeToWorld = _appRenderArgs._eyeToWorld; sensorToWorld = _appRenderArgs._sensorToWorld; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 5d8393ba7a..e8b800db69 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -53,7 +53,7 @@ const QUuid MY_AVATAR_KEY; // NULL key AvatarManager::AvatarManager(QObject* parent) : _avatarsToFade(), - _myAvatar(std::make_shared(qApp->thread())) + _myAvatar(new MyAvatar(qApp->thread()), [](MyAvatar* ptr) { ptr->deleteLater(); }) { // register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar qRegisterMetaType >("NodeWeakPointer"); @@ -297,7 +297,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { } AvatarSharedPointer AvatarManager::newSharedAvatar() { - return std::make_shared(qApp->thread()); + return AvatarSharedPointer(new OtherAvatar(qApp->thread()), [](OtherAvatar* ptr) { ptr->deleteLater(); }); } void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 10e2202553..5d82405aee 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3238,3 +3238,9 @@ void MyAvatar::setModelScale(float scale) { emit sensorToWorldScaleChanged(sensorToWorldScale); } } + +SpatialParentTree* MyAvatar::getParentTree() const { + auto entityTreeRenderer = qApp->getEntities(); + EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr; + return entityTree.get(); +} diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 9620d61a49..952315a85f 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -544,6 +544,8 @@ public: float getUserHeight() const; float getUserEyeHeight() const; + virtual SpatialParentTree* getParentTree() const override; + public slots: void increaseSize(); void decreaseSize(); diff --git a/interface/src/scripting/ControllerScriptingInterface.cpp b/interface/src/scripting/ControllerScriptingInterface.cpp index 5c55e2094b..4848531de7 100644 --- a/interface/src/scripting/ControllerScriptingInterface.cpp +++ b/interface/src/scripting/ControllerScriptingInterface.cpp @@ -13,23 +13,10 @@ #include #include -#include #include #include "Application.h" -void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) { - if (event->type() == HFActionEvent::startType()) { - emit actionStartEvent(static_cast(*event)); - } else if (event->type() == HFActionEvent::endType()) { - emit actionEndEvent(static_cast(*event)); - } else if (event->type() == HFBackEvent::startType()) { - emit backStartEvent(); - } else if (event->type() == HFBackEvent::endType()) { - emit backEndEvent(); - } -} - bool ControllerScriptingInterface::isKeyCaptured(QKeyEvent* event) const { return isKeyCaptured(KeyEvent(*event)); } diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h index 8c825a17de..7a2c964622 100644 --- a/interface/src/scripting/ControllerScriptingInterface.h +++ b/interface/src/scripting/ControllerScriptingInterface.h @@ -17,7 +17,6 @@ #include #include -#include #include #include #include @@ -36,8 +35,6 @@ public: void emitKeyPressEvent(QKeyEvent* event); void emitKeyReleaseEvent(QKeyEvent* event); - void handleMetaEvent(HFMetaEvent* event); - void emitMouseMoveEvent(QMouseEvent* event); void emitMousePressEvent(QMouseEvent* event); void emitMouseDoublePressEvent(QMouseEvent* event); @@ -72,12 +69,6 @@ signals: void keyPressEvent(const KeyEvent& event); void keyReleaseEvent(const KeyEvent& event); - void actionStartEvent(const HFActionEvent& event); - void actionEndEvent(const HFActionEvent& event); - - void backStartEvent(); - void backEndEvent(); - void mouseMoveEvent(const MouseEvent& event); void mousePressEvent(const MouseEvent& event); void mouseDoublePressEvent(const MouseEvent& event); 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/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index a34240483b..42425932df 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -305,3 +305,9 @@ Transform Base3DOverlay::evalRenderTransform() { void Base3DOverlay::setRenderTransform(const Transform& transform) { _renderTransform = transform; } + +SpatialParentTree* Base3DOverlay::getParentTree() const { + auto entityTreeRenderer = qApp->getEntities(); + EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr; + return entityTree.get(); +} diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index d90caf5c4d..436cfbf97b 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -55,7 +55,7 @@ public: virtual AABox getBounds() const override = 0; void update(float deltatime) override; - + void notifyRenderTransformChange() const; void setProperties(const QVariantMap& properties) override; @@ -69,6 +69,8 @@ public: return findRayIntersection(origin, direction, distance, face, surfaceNormal); } + virtual SpatialParentTree* getParentTree() const override; + protected: virtual void locationChanged(bool tellPhysics = true) override; virtual void parentDeleted() override; diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index b59bcdb9b2..5e5b9367a6 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -161,33 +161,33 @@ OverlayID Overlays::addOverlay(const QString& type, const QVariant& properties) Overlay::Pointer thisOverlay = nullptr; if (type == ImageOverlay::TYPE) { - thisOverlay = std::make_shared(); + thisOverlay = Overlay::Pointer(new ImageOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); } else if (type == Image3DOverlay::TYPE || type == "billboard") { // "billboard" for backwards compatibility - thisOverlay = std::make_shared(); + thisOverlay = Overlay::Pointer(new Image3DOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); } else if (type == TextOverlay::TYPE) { - thisOverlay = std::make_shared(); + thisOverlay = Overlay::Pointer(new TextOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); } else if (type == Text3DOverlay::TYPE) { - thisOverlay = std::make_shared(); + thisOverlay = Overlay::Pointer(new Text3DOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); } else if (type == Shape3DOverlay::TYPE) { - thisOverlay = std::make_shared(); + thisOverlay = Overlay::Pointer(new Shape3DOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); } else if (type == Cube3DOverlay::TYPE) { - thisOverlay = std::make_shared(); + thisOverlay = Overlay::Pointer(new Cube3DOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); } else if (type == Sphere3DOverlay::TYPE) { - thisOverlay = std::make_shared(); + thisOverlay = Overlay::Pointer(new Sphere3DOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); } else if (type == Circle3DOverlay::TYPE) { - thisOverlay = std::make_shared(); + thisOverlay = Overlay::Pointer(new Circle3DOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); } else if (type == Rectangle3DOverlay::TYPE) { - thisOverlay = std::make_shared(); + thisOverlay = Overlay::Pointer(new Rectangle3DOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); } else if (type == Line3DOverlay::TYPE) { - thisOverlay = std::make_shared(); + thisOverlay = Overlay::Pointer(new Line3DOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); } else if (type == Grid3DOverlay::TYPE) { - thisOverlay = std::make_shared(); + thisOverlay = Overlay::Pointer(new Grid3DOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); } else if (type == ModelOverlay::TYPE) { - thisOverlay = std::make_shared(); + thisOverlay = Overlay::Pointer(new ModelOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); } else if (type == Web3DOverlay::TYPE) { - thisOverlay = std::make_shared(); + thisOverlay = Overlay::Pointer(new Web3DOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); } else if (type == RectangleOverlay::TYPE) { - thisOverlay = std::make_shared(); + thisOverlay = Overlay::Pointer(new RectangleOverlay(), [](Overlay* ptr) { ptr->deleteLater(); }); } if (thisOverlay) { @@ -230,7 +230,7 @@ OverlayID Overlays::cloneOverlay(OverlayID id) { Overlay::Pointer thisOverlay = getOverlay(id); if (thisOverlay) { - OverlayID cloneId = addOverlay(Overlay::Pointer(thisOverlay->createClone())); + OverlayID cloneId = addOverlay(Overlay::Pointer(thisOverlay->createClone(), [](Overlay* ptr) { ptr->deleteLater(); })); #if OVERLAY_PANELS auto attachable = std::dynamic_pointer_cast(thisOverlay); if (attachable && attachable->getParentPanel()) { diff --git a/libraries/audio/src/InboundAudioStream.cpp b/libraries/audio/src/InboundAudioStream.cpp index da8997895c..72acc7fcf6 100644 --- a/libraries/audio/src/InboundAudioStream.cpp +++ b/libraries/audio/src/InboundAudioStream.cpp @@ -46,6 +46,10 @@ static const int STATS_FOR_STATS_PACKET_WINDOW_SECONDS = 30; // _currentJitterBufferFrames is updated with the time-weighted avg and the running time-weighted avg is reset. static const quint64 FRAMES_AVAILABLE_STAT_WINDOW_USECS = 10 * USECS_PER_SECOND; +// When the audio codec is switched, temporary codec mismatch is expected due to packets in-flight. +// A SelectedAudioFormat packet is not sent until this threshold is exceeded. +static const int MAX_MISMATCHED_AUDIO_CODEC_COUNT = 10; + InboundAudioStream::InboundAudioStream(int numChannels, int numFrames, int numBlocks, int numStaticJitterBlocks) : _ringBuffer(numChannels * numFrames, numBlocks), _numChannels(numChannels), @@ -153,6 +157,7 @@ int InboundAudioStream::parseData(ReceivedMessage& message) { // If we recieved a SilentAudioFrame from our sender, we might want to drop // some of the samples in order to catch up to our desired jitter buffer size. writeDroppableSilentFrames(networkFrames); + } else { // note: PCM and no codec are identical bool selectedPCM = _selectedCodecName == "pcm" || _selectedCodecName == ""; @@ -160,20 +165,33 @@ int InboundAudioStream::parseData(ReceivedMessage& message) { if (codecInPacket == _selectedCodecName || (packetPCM && selectedPCM)) { auto afterProperties = message.readWithoutCopy(message.getBytesLeftToRead()); parseAudioData(message.getType(), afterProperties); + _mismatchedAudioCodecCount = 0; + } else { - qDebug(audio) << "Codec mismatch: expected" << _selectedCodecName << "got" << codecInPacket << "writing silence"; + _mismatchedAudioCodecCount++; + qDebug(audio) << "Codec mismatch: expected" << _selectedCodecName << "got" << codecInPacket; - // Since the data in the stream is using a codec that we aren't prepared for, - // we need to let the codec know that we don't have data for it, this will - // allow the codec to interpolate missing data and produce a fade to silence. - lostAudioData(1); - - // inform others of the mismatch - auto sendingNode = DependencyManager::get()->nodeWithUUID(message.getSourceID()); - if (sendingNode) { - emit mismatchedAudioCodec(sendingNode, _selectedCodecName, codecInPacket); + if (packetPCM) { + // If there are PCM packets in-flight after the codec is changed, use them. + auto afterProperties = message.readWithoutCopy(message.getBytesLeftToRead()); + _ringBuffer.writeData(afterProperties.data(), afterProperties.size()); + } else { + // Since the data in the stream is using a codec that we aren't prepared for, + // we need to let the codec know that we don't have data for it, this will + // allow the codec to interpolate missing data and produce a fade to silence. + lostAudioData(1); } + if (_mismatchedAudioCodecCount > MAX_MISMATCHED_AUDIO_CODEC_COUNT) { + _mismatchedAudioCodecCount = 0; + + // inform others of the mismatch + auto sendingNode = DependencyManager::get()->nodeWithUUID(message.getSourceID()); + if (sendingNode) { + emit mismatchedAudioCodec(sendingNode, _selectedCodecName, codecInPacket); + qDebug(audio) << "Codec mismatch threshold exceeded, SelectedAudioFormat(" << _selectedCodecName << " ) sent"; + } + } } } break; diff --git a/libraries/audio/src/InboundAudioStream.h b/libraries/audio/src/InboundAudioStream.h index 9494b2f204..ecd1a118f9 100644 --- a/libraries/audio/src/InboundAudioStream.h +++ b/libraries/audio/src/InboundAudioStream.h @@ -186,6 +186,7 @@ protected: CodecPluginPointer _codec; QString _selectedCodecName; Decoder* _decoder { nullptr }; + int _mismatchedAudioCodecCount { 0 }; }; float calculateRepeatedFrameFadeFactor(int indexOfRepeat); diff --git a/libraries/baking/src/JSBaker.cpp b/libraries/baking/src/JSBaker.cpp new file mode 100644 index 0000000000..75811bea49 --- /dev/null +++ b/libraries/baking/src/JSBaker.cpp @@ -0,0 +1,247 @@ +// +// JSBaker.cpp +// libraries/baking/src +// +// Created by Utkarsh Gautam on 9/18/17. +// 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 +// + +#include + +#include "JSBaker.h" +#include "Baker.h" + +const int ASCII_CHARACTERS_UPPER_LIMIT = 126; + +JSBaker::JSBaker(const QUrl& jsURL, const QString& bakedOutputDir) : + _jsURL(jsURL), + _bakedOutputDir(bakedOutputDir) +{ + +} + +void JSBaker::bake() { + qCDebug(js_baking) << "JS Baker " << _jsURL << "bake starting"; + + // Import file to start baking + QFile jsFile(_jsURL.toLocalFile()); + if (!jsFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + handleError("Error opening " + _jsURL.fileName() + " for reading"); + return; + } + + // Read file into an array + QByteArray inputJS = jsFile.readAll(); + QByteArray outputJS; + + // Call baking on inputJS and store result in outputJS + bool success = bakeJS(inputJS, outputJS); + if (!success) { + qCDebug(js_baking) << "Bake Failed"; + handleError("Unterminated multi-line comment"); + return; + } + + // Bake Successful. Export the file + auto fileName = _jsURL.fileName(); + auto baseName = fileName.left(fileName.lastIndexOf('.')); + auto bakedFilename = baseName + BAKED_JS_EXTENSION; + + _bakedJSFilePath = _bakedOutputDir + "/" + bakedFilename; + + QFile bakedFile; + bakedFile.setFileName(_bakedJSFilePath); + if (!bakedFile.open(QIODevice::WriteOnly)) { + handleError("Error opening " + _bakedJSFilePath + " for writing"); + return; + } + + bakedFile.write(outputJS); + + // Export successful + _outputFiles.push_back(_bakedJSFilePath); + qCDebug(js_baking) << "Exported" << _jsURL << "minified to" << _bakedJSFilePath; + + // emit signal to indicate the JS baking is finished + emit finished(); +} + +bool JSBaker::bakeJS(const QByteArray& inputFile, QByteArray& outputFile) { + // Read from inputFile and write to outputFile per character + QTextStream in(inputFile, QIODevice::ReadOnly); + QTextStream out(outputFile, QIODevice::WriteOnly); + + // Algorithm requires the knowledge of previous and next character for each character read + QChar currentCharacter; + QChar nextCharacter; + // Initialize previousCharacter with new line + QChar previousCharacter = '\n'; + + in >> currentCharacter; + + while (!in.atEnd()) { + in >> nextCharacter; + + if (currentCharacter == '\r') { + out << '\n'; + } else if (currentCharacter == '/') { + // Check if single line comment i.e. // + if (nextCharacter == '/') { + handleSingleLineComments(in); + + //Start fresh after handling comments + previousCharacter = '\n'; + in >> currentCharacter; + continue; + } else if (nextCharacter == '*') { + // Check if multi line comment i.e. /* + bool success = handleMultiLineComments(in); + if (!success) { + // Errors present return false + return false; + } + //Start fresh after handling comments + previousCharacter = '\n'; + in >> currentCharacter; + continue; + } else { + // If '/' is not followed by '/' or '*' print '/' + out << currentCharacter; + } + } else if (isSpaceOrTab(currentCharacter)) { + // Check if white space or tab + + // Skip multiple spaces or tabs + while (isSpaceOrTab(nextCharacter)) { + in >> nextCharacter; + if (nextCharacter == '\n') { + break; + } + } + + // check if space can be omitted + if (!canOmitSpace(previousCharacter, nextCharacter)) { + out << ' '; + } + } else if (currentCharacter == '\n') { + // Check if new line + + //Skip multiple new lines + //Skip new line followed by space or tab + while (nextCharacter == '\n' || isSpaceOrTab(nextCharacter)) { + in >> nextCharacter; + } + + // Check if new line can be omitted + if (!canOmitNewLine(previousCharacter, nextCharacter)) { + out << '\n'; + } + } else if (isQuote(currentCharacter)) { + // Print the current quote and nextCharacter as is + out << currentCharacter; + out << nextCharacter; + + // Store the type of quote we are processing + QChar quote = currentCharacter; + + // Don't modify the quoted strings + while (nextCharacter != quote) { + in >> nextCharacter; + out << nextCharacter; + } + + //Start fresh after handling quoted strings + previousCharacter = nextCharacter; + in >> currentCharacter; + continue; + } else { + // In all other cases write the currentCharacter to outputFile + out << currentCharacter; + } + + previousCharacter = currentCharacter; + currentCharacter = nextCharacter; + } + + //write currentCharacter to output file when nextCharacter reaches EOF + if (currentCharacter != '\n') { + out << currentCharacter; + } + + // Successful bake. Return true + return true; +} + +void JSBaker::handleSingleLineComments(QTextStream& in) { + QChar character; + while (!in.atEnd()) { + in >> character; + if (character == '\n') { + break; + } + } +} + +bool JSBaker::handleMultiLineComments(QTextStream& in) { + QChar character; + while (!in.atEnd()) { + in >> character; + if (character == '*') { + if (in.read(1) == '/') { + return true; + } + } + } + return false; +} + +bool JSBaker::canOmitSpace(QChar previousCharacter, QChar nextCharacter) { + return(!((isAlphanum(previousCharacter) || isNonAscii(previousCharacter) || isSpecialCharacter(previousCharacter)) && + (isAlphanum(nextCharacter) || isNonAscii(nextCharacter) || isSpecialCharacter(nextCharacter))) + ); +} + +bool JSBaker::canOmitNewLine(QChar previousCharacter, QChar nextCharacter) { + return (!((isAlphanum(previousCharacter) || isNonAscii(previousCharacter) || isSpecialCharacterPrevious(previousCharacter)) && + (isAlphanum(nextCharacter) || isNonAscii(nextCharacter) || isSpecialCharacterNext(nextCharacter))) + ); +} + +//Check if character is alphabet, number or one of the following: '_', '$', '\\' or a non-ASCII character +bool JSBaker::isAlphanum(QChar c) { + return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') + || c == '_' || c == '$' || c == '\\' || c > ASCII_CHARACTERS_UPPER_LIMIT); +} + +bool JSBaker::isNonAscii(QChar c) { + return ((int)c.toLatin1() > ASCII_CHARACTERS_UPPER_LIMIT); +} + +// If previous and next characters are special characters, don't omit space +bool JSBaker::isSpecialCharacter(QChar c) { + return (c == '\'' || c == '$' || c == '_' || c == '/' || c== '+' || c == '-'); +} + +// If previous character is a special character, maybe don't omit new line (depends on next character as well) +bool JSBaker::isSpecialCharacterPrevious(QChar c) { + return (c == '\'' || c == '$' || c == '_' || c == '}' || c == ']' || c == ')' || c == '+' || c == '-' + || c == '"' || c == "'"); +} + +// If next character is a special character, maybe don't omit new line (depends on previous character as well) +bool JSBaker::isSpecialCharacterNext(QChar c) { + return (c == '\'' || c == '$' || c == '_' || c == '{' || c == '[' || c == '(' || c == '+' || c == '-'); +} + +// Check if white space or tab +bool JSBaker::isSpaceOrTab(QChar c) { + return (c == ' ' || c == '\t'); +} + +// Check If the currentCharacter is " or ' or ` +bool JSBaker::isQuote(QChar c) { + return (c == '"' || c == "'" || c == '`'); +} diff --git a/libraries/baking/src/JSBaker.h b/libraries/baking/src/JSBaker.h new file mode 100644 index 0000000000..b5889440cb --- /dev/null +++ b/libraries/baking/src/JSBaker.h @@ -0,0 +1,49 @@ +// +// JSBaker.h +// libraries/baking/src +// +// Created by Utkarsh Gautam on 9/18/17. +// 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 +// + +#ifndef hifi_JSBaker_h +#define hifi_JSBaker_h + +#include "Baker.h" +#include "JSBakingLoggingCategory.h" + +static const QString BAKED_JS_EXTENSION = ".baked.js"; + +class JSBaker : public Baker { + Q_OBJECT +public: + JSBaker(const QUrl& jsURL, const QString& bakedOutputDir); + static bool bakeJS(const QByteArray& inputFile, QByteArray& outputFile); + +public slots: + virtual void bake() override; + +private: + QUrl _jsURL; + QString _bakedOutputDir; + QString _bakedJSFilePath; + + static void handleSingleLineComments(QTextStream& in); + static bool handleMultiLineComments(QTextStream& in); + + static bool canOmitSpace(QChar previousCharacter, QChar nextCharacter); + static bool canOmitNewLine(QChar previousCharacter, QChar nextCharacter); + + static bool isAlphanum(QChar c); + static bool isNonAscii(QChar c); + static bool isSpecialCharacter(QChar c); + static bool isSpecialCharacterPrevious(QChar c); + static bool isSpecialCharacterNext(QChar c); + static bool isSpaceOrTab(QChar c); + static bool isQuote(QChar c); +}; + +#endif // !hifi_JSBaker_h diff --git a/libraries/baking/src/JSBakingLoggingCategory.cpp b/libraries/baking/src/JSBakingLoggingCategory.cpp new file mode 100644 index 0000000000..77c667922f --- /dev/null +++ b/libraries/baking/src/JSBakingLoggingCategory.cpp @@ -0,0 +1,14 @@ +// +// JSBakingLoggingCategory.cpp +// libraries/baking/src +// +// Created by Utkarsh Gautam on 9/18/17. +// 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 +// + +#include "JSBakingLoggingCategory.h" + +Q_LOGGING_CATEGORY(js_baking, "hifi.JS-baking"); diff --git a/libraries/baking/src/JSBakingLoggingCategory.h b/libraries/baking/src/JSBakingLoggingCategory.h new file mode 100644 index 0000000000..2c3ff535af --- /dev/null +++ b/libraries/baking/src/JSBakingLoggingCategory.h @@ -0,0 +1,19 @@ +// +// JSBakingLoggingCategory.h +// libraries/baking/src +// +// Created by Utkarsh Gautam on 9/18/17. +// 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 +// + +#ifndef hifi_JSBakingLoggingCategory_h +#define hifi_JSBakingLoggingCategory_h + +#include + +Q_DECLARE_LOGGING_CATEGORY(js_baking) + +#endif // hifi_ModelBakingLoggingCategory_h diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 92a82908e2..77a9b8f9c6 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -241,19 +241,7 @@ void EntityTreeRenderer::update(bool simulate) { EntityTreePointer tree = std::static_pointer_cast(_tree); tree->update(simulate); - if (simulate) { - // Handle enter/leave entity logic - checkEnterLeaveEntities(); - - // Even if we're not moving the mouse, if we started clicking on an entity and we have - // not yet released the hold then this is still considered a holdingClickOnEntity event - // and we want to simulate this message here as well as in mouse move - if (_lastPointerEventValid && !_currentClickingOnEntityID.isInvalidID()) { - emit holdingClickOnEntity(_currentClickingOnEntityID, _lastPointerEvent); - _entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "holdingClickOnEntity", _lastPointerEvent); - } - } - + // Update the rendereable entities as needed { PerformanceTimer sceneTimer("scene"); auto scene = _viewState->getMain3DScene(); @@ -273,6 +261,20 @@ void EntityTreeRenderer::update(bool simulate) { } } } + + if (simulate) { + // Handle enter/leave entity logic + checkEnterLeaveEntities(); + + // Even if we're not moving the mouse, if we started clicking on an entity and we have + // not yet released the hold then this is still considered a holdingClickOnEntity event + // and we want to simulate this message here as well as in mouse move + if (_lastPointerEventValid && !_currentClickingOnEntityID.isInvalidID()) { + emit holdingClickOnEntity(_currentClickingOnEntityID, _lastPointerEvent); + _entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "holdingClickOnEntity", _lastPointerEvent); + } + } + } } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 9120cd1788..e9d395a857 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -60,7 +60,8 @@ bool ModelEntityWrapper::isModelLoaded() const { } EntityItemPointer RenderableModelEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - EntityItemPointer entity{ new RenderableModelEntityItem(entityID, properties.getDimensionsInitialized()) }; + EntityItemPointer entity(new RenderableModelEntityItem(entityID, properties.getDimensionsInitialized()), + [](EntityItem* ptr) { ptr->deleteLater(); }); entity->setProperties(properties); return entity; } diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 4c254980c4..b11ab76c2f 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -138,7 +138,7 @@ void loop3(const T& start, const T& end, F f) { } EntityItemPointer RenderablePolyVoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - std::shared_ptr entity{ new RenderablePolyVoxEntityItem(entityID) }; + std::shared_ptr entity(new RenderablePolyVoxEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); entity->setProperties(properties); entity->initializePolyVox(); return entity; diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index ee0fcf8218..f93b6a49ec 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -45,7 +45,7 @@ void EntityEditPacketSender::queueEditAvatarEntityMessage(PacketType type, } EntityItemPointer entity = entityTree->findEntityByEntityItemID(entityItemID); if (!entity) { - qCDebug(entities) << "EntityEditPacketSender::queueEditEntityMessage can't find entity."; + qCDebug(entities) << "EntityEditPacketSender::queueEditAvatarEntityMessage can't find entity: " << entityItemID; return; } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index fa386ae090..d16aeaa6e1 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1848,7 +1848,13 @@ bool EntityTree::sendEntitiesOperation(const OctreeElementPointer& element, void QHash::iterator iter = args->map->find(oldID); if (iter == args->map->end()) { - EntityItemID newID = QUuid::createUuid(); + EntityItemID newID; + if (oldID == AVATAR_SELF_ID) { + auto nodeList = DependencyManager::get(); + newID = EntityItemID(nodeList->getSessionUUID()); + } else { + newID = QUuid::createUuid(); + } args->map->insert(oldID, newID); return newID; } @@ -1865,8 +1871,8 @@ bool EntityTree::sendEntitiesOperation(const OctreeElementPointer& element, void properties.setPosition(properties.getPosition() + args->root); } else { EntityItemPointer parentEntity = args->ourTree->findEntityByEntityItemID(oldParentID); - if (parentEntity) { // map the parent - properties.setParentID(getMapped(parentEntity->getID())); + if (parentEntity || oldParentID == AVATAR_SELF_ID) { // map the parent + properties.setParentID(getMapped(oldParentID)); // But do not add root offset in this case. } else { // Should not happen, but let's try to be helpful... item->globalizeProperties(properties, "Cannot find %3 parent of %2 %1", args->root); @@ -1941,6 +1947,12 @@ bool EntityTree::readFromMap(QVariantMap& map) { entityItemID = EntityItemID(QUuid::createUuid()); } + if (properties.getClientOnly()) { + auto nodeList = DependencyManager::get(); + const QUuid myNodeID = nodeList->getSessionUUID(); + properties.setOwningAvatarID(myNodeID); + } + EntityItemPointer entity = addEntity(entityItemID, properties); if (!entity) { qCDebug(entities) << "adding Entity failed:" << entityItemID << properties.getType(); diff --git a/libraries/entities/src/LightEntityItem.cpp b/libraries/entities/src/LightEntityItem.cpp index ce530400ef..e1ccf8556b 100644 --- a/libraries/entities/src/LightEntityItem.cpp +++ b/libraries/entities/src/LightEntityItem.cpp @@ -30,7 +30,7 @@ const float LightEntityItem::DEFAULT_CUTOFF = PI / 2.0f; bool LightEntityItem::_lightsArePickable = false; EntityItemPointer LightEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - EntityItemPointer entity { new LightEntityItem(entityID) }; + EntityItemPointer entity(new LightEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); entity->setProperties(properties); return entity; } diff --git a/libraries/entities/src/LineEntityItem.cpp b/libraries/entities/src/LineEntityItem.cpp index 8d133126ab..119236e32d 100644 --- a/libraries/entities/src/LineEntityItem.cpp +++ b/libraries/entities/src/LineEntityItem.cpp @@ -26,7 +26,7 @@ const int LineEntityItem::MAX_POINTS_PER_LINE = 70; EntityItemPointer LineEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - EntityItemPointer entity { new LineEntityItem(entityID) }; + EntityItemPointer entity(new LineEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); entity->setProperties(properties); return entity; } @@ -214,4 +214,4 @@ void LineEntityItem::resetPointsChanged() { withWriteLock([&] { _pointsChanged = false; }); -} \ No newline at end of file +} diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 9c3ce47886..6af4db154a 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -26,7 +26,7 @@ const QString ModelEntityItem::DEFAULT_MODEL_URL = QString(""); const QString ModelEntityItem::DEFAULT_COMPOUND_SHAPE_URL = QString(""); EntityItemPointer ModelEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - EntityItemPointer entity { new ModelEntityItem(entityID) }; + EntityItemPointer entity(new ModelEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); entity->setProperties(properties); return entity; } diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index c6616f8cd3..1a815de632 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -147,7 +147,7 @@ uint64_t Properties::emitIntervalUsecs() const { EntityItemPointer ParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - EntityItemPointer entity { new ParticleEffectEntityItem(entityID) }; + EntityItemPointer entity(new ParticleEffectEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); entity->setProperties(properties); return entity; } diff --git a/libraries/entities/src/PolyLineEntityItem.cpp b/libraries/entities/src/PolyLineEntityItem.cpp index ad9686bdf2..a308a17c66 100644 --- a/libraries/entities/src/PolyLineEntityItem.cpp +++ b/libraries/entities/src/PolyLineEntityItem.cpp @@ -26,7 +26,7 @@ const int PolyLineEntityItem::MAX_POINTS_PER_LINE = 70; EntityItemPointer PolyLineEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - EntityItemPointer entity{ new PolyLineEntityItem(entityID) }; + EntityItemPointer entity(new PolyLineEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); entity->setProperties(properties); return entity; } diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index daf7ca3f79..e577a6c1a7 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -47,7 +47,7 @@ const QString PolyVoxEntityItem::DEFAULT_Y_TEXTURE_URL = QString(""); const QString PolyVoxEntityItem::DEFAULT_Z_TEXTURE_URL = QString(""); EntityItemPointer PolyVoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - EntityItemPointer entity { new PolyVoxEntityItem(entityID) }; + EntityItemPointer entity(new PolyVoxEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); entity->setProperties(properties); return entity; } diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index 586344ee81..6e3bdc27a4 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -52,7 +52,7 @@ namespace entity { } ShapeEntityItem::Pointer ShapeEntityItem::baseFactory(const EntityItemID& entityID, const EntityItemProperties& properties) { - Pointer entity { new ShapeEntityItem(entityID) }; + Pointer entity(new ShapeEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); entity->setProperties(properties); return entity; } diff --git a/libraries/entities/src/TextEntityItem.cpp b/libraries/entities/src/TextEntityItem.cpp index 3ade5879c5..074691e1d4 100644 --- a/libraries/entities/src/TextEntityItem.cpp +++ b/libraries/entities/src/TextEntityItem.cpp @@ -30,7 +30,7 @@ const xColor TextEntityItem::DEFAULT_BACKGROUND_COLOR = { 0, 0, 0}; const bool TextEntityItem::DEFAULT_FACE_CAMERA = false; EntityItemPointer TextEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - EntityItemPointer entity { new TextEntityItem(entityID) }; + EntityItemPointer entity(new TextEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); entity->setProperties(properties); return entity; } diff --git a/libraries/entities/src/WebEntityItem.cpp b/libraries/entities/src/WebEntityItem.cpp index 9595f2959c..dd4bf518e0 100644 --- a/libraries/entities/src/WebEntityItem.cpp +++ b/libraries/entities/src/WebEntityItem.cpp @@ -24,7 +24,7 @@ const QString WebEntityItem::DEFAULT_SOURCE_URL("http://www.google.com"); EntityItemPointer WebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - EntityItemPointer entity { new WebEntityItem(entityID) }; + EntityItemPointer entity(new WebEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); entity->setProperties(properties); return entity; } diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index 88e4f3c9e6..13a1bbac43 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -32,7 +32,7 @@ const bool ZoneEntityItem::DEFAULT_GHOSTING_ALLOWED = true; const QString ZoneEntityItem::DEFAULT_FILTER_URL = ""; EntityItemPointer ZoneEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { - EntityItemPointer entity { new ZoneEntityItem(entityID) }; + EntityItemPointer entity(new ZoneEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); entity->setProperties(properties); return entity; } diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 58b920da4d..5959714cd8 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -983,6 +983,9 @@ public: glm::vec2 dstCoord; glm::ivec2 srcPixel; for (int y = 0; y < faceWidth; ++y) { + QRgb* destScanLineBegin = reinterpret_cast( image.scanLine(y) ); + QRgb* destPixelIterator = destScanLineBegin; + dstCoord.y = 1.0f - (y + 0.5f) * dstInvSize.y; // Fill cube face images from top to bottom for (int x = 0; x < faceWidth; ++x) { dstCoord.x = (x + 0.5f) * dstInvSize.x; @@ -995,13 +998,19 @@ public: srcPixel.y = floor((1.0f - srcCoord.y) * srcFaceHeight); if (((uint32)srcPixel.x < (uint32)source.width()) && ((uint32)srcPixel.y < (uint32)source.height())) { - image.setPixel(x, y, source.pixel(QPoint(srcPixel.x, srcPixel.y))); + // We can't directly use the pixel() method because that launches a pixel color conversion to output + // a correct RGBA8 color. But in our case we may have stored HDR values encoded in a RGB30 format which + // are not convertible by Qt. The same goes with the setPixel method, by the way. + const QRgb* sourcePixelIterator = reinterpret_cast(source.scanLine(srcPixel.y)); + sourcePixelIterator += srcPixel.x; + *destPixelIterator = *sourcePixelIterator; // Keep for debug, this is showing the dir as a color // glm::u8vec4 rgba((xyzDir.x + 1.0)*0.5 * 256, (xyzDir.y + 1.0)*0.5 * 256, (xyzDir.z + 1.0)*0.5 * 256, 256); // unsigned int val = 0xff000000 | (rgba.r) | (rgba.g << 8) | (rgba.b << 16); - // image.setPixel(x, y, val); + // *destPixelIterator = val; } + ++destPixelIterator; } } return image; @@ -1192,6 +1201,10 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(const QImage& if ((srcImage.width() > 0) && (srcImage.height() > 0)) { QImage image = processSourceImage(srcImage, true); + if (image.format() != QIMAGE_HDR_FORMAT) { + image = convertToHDRFormat(image, HDR_FORMAT); + } + gpu::Element formatMip; gpu::Element formatGPU; if (isCubeTexturesCompressionEnabled()) { @@ -1202,10 +1215,6 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(const QImage& formatGPU = HDR_FORMAT; } - if (image.format() != QIMAGE_HDR_FORMAT) { - image = convertToHDRFormat(image, HDR_FORMAT); - } - // Find the layout of the cubemap in the 2D image // Use the original image size since processSourceImage may have altered the size / aspect ratio int foundLayout = CubeLayout::findLayout(srcImage.width(), srcImage.height()); 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/libraries/script-engine/src/EventTypes.cpp b/libraries/script-engine/src/EventTypes.cpp index 1bdf0f5034..abdd934e5a 100644 --- a/libraries/script-engine/src/EventTypes.cpp +++ b/libraries/script-engine/src/EventTypes.cpp @@ -9,7 +9,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "HFActionEvent.h" #include "KeyEvent.h" #include "MouseEvent.h" #include "SpatialEvent.h" @@ -20,7 +19,6 @@ #include "EventTypes.h" void registerEventTypes(QScriptEngine* engine) { - qScriptRegisterMetaType(engine, HFActionEvent::toScriptValue, HFActionEvent::fromScriptValue); qScriptRegisterMetaType(engine, KeyEvent::toScriptValue, KeyEvent::fromScriptValue); qScriptRegisterMetaType(engine, MouseEvent::toScriptValue, MouseEvent::fromScriptValue); qScriptRegisterMetaType(engine, PointerEvent::toScriptValue, PointerEvent::fromScriptValue); diff --git a/libraries/script-engine/src/HFActionEvent.cpp b/libraries/script-engine/src/HFActionEvent.cpp deleted file mode 100644 index 3ed3bcb73c..0000000000 --- a/libraries/script-engine/src/HFActionEvent.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// -// HFActionEvent.cpp -// script-engine/src -// -// Created by Stephen Birarda on 2014-10-27. -// Copyright 2014 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 -// - -#include "HFActionEvent.h" - -HFActionEvent::HFActionEvent(QEvent::Type type, const PickRay& actionRay) : - HFMetaEvent(type), - actionRay(actionRay) -{ - -} - -QEvent::Type HFActionEvent::startType() { - static QEvent::Type startType = HFMetaEvent::newEventType(); - return startType; -} - -QEvent::Type HFActionEvent::endType() { - static QEvent::Type endType = HFMetaEvent::newEventType(); - return endType; -} - -QScriptValue HFActionEvent::toScriptValue(QScriptEngine* engine, const HFActionEvent& event) { - QScriptValue obj = engine->newObject(); - obj.setProperty("actionRay", pickRayToScriptValue(engine, event.actionRay)); - return obj; -} - -void HFActionEvent::fromScriptValue(const QScriptValue& object, HFActionEvent& event) { - // not yet implemented -} - diff --git a/libraries/script-engine/src/HFActionEvent.h b/libraries/script-engine/src/HFActionEvent.h deleted file mode 100644 index c16b165ae3..0000000000 --- a/libraries/script-engine/src/HFActionEvent.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// HFActionEvent.h -// script-engine/src -// -// Created by Stephen Birarda on 2014-10-27. -// Copyright 2014 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 -// - -#ifndef hifi_HFActionEvent_h -#define hifi_HFActionEvent_h - - -#include - -#include - -#include "HFMetaEvent.h" - -class HFActionEvent : public HFMetaEvent { -public: - HFActionEvent() {}; - HFActionEvent(QEvent::Type type, const PickRay& actionRay); - - static QEvent::Type startType(); - static QEvent::Type endType(); - - static QScriptValue toScriptValue(QScriptEngine* engine, const HFActionEvent& event); - static void fromScriptValue(const QScriptValue& object, HFActionEvent& event); - - PickRay actionRay; -}; - -Q_DECLARE_METATYPE(HFActionEvent) - -#endif // hifi_HFActionEvent_h \ No newline at end of file diff --git a/libraries/script-engine/src/HFBackEvent.cpp b/libraries/script-engine/src/HFBackEvent.cpp deleted file mode 100644 index c67b2e3431..0000000000 --- a/libraries/script-engine/src/HFBackEvent.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// -// HFBackEvent.cpp -// script-engine/src -// -// Created by Stephen Birarda on 2014-10-27. -// Copyright 2014 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 -// - -#include "HFBackEvent.h" - -HFBackEvent::HFBackEvent(QEvent::Type type) : - HFMetaEvent(type) -{ - -} - -QEvent::Type HFBackEvent::startType() { - static QEvent::Type startType = HFMetaEvent::newEventType(); - return startType; -} - -QEvent::Type HFBackEvent::endType() { - static QEvent::Type endType = HFMetaEvent::newEventType(); - return endType; -} diff --git a/libraries/script-engine/src/HFBackEvent.h b/libraries/script-engine/src/HFBackEvent.h deleted file mode 100644 index bb7b348ea7..0000000000 --- a/libraries/script-engine/src/HFBackEvent.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// HFBackEvent.h -// script-engine/src -// -// Created by Stephen Birarda on 2014-10-27. -// Copyright 2014 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 -// - -#ifndef hifi_HFBackEvent_h -#define hifi_HFBackEvent_h - -#include -#include - -#include "HFMetaEvent.h" - -class HFBackEvent : public HFMetaEvent { -public: - HFBackEvent() {}; - HFBackEvent(QEvent::Type type); - - static QEvent::Type startType(); - static QEvent::Type endType(); -}; - -#endif // hifi_HFBackEvent_h \ No newline at end of file diff --git a/libraries/script-engine/src/HFMetaEvent.cpp b/libraries/script-engine/src/HFMetaEvent.cpp deleted file mode 100644 index f06c349996..0000000000 --- a/libraries/script-engine/src/HFMetaEvent.cpp +++ /dev/null @@ -1,20 +0,0 @@ -// -// HFMetaEvent.cpp -// script-engine/src -// -// Created by Stephen Birarda on 2014-10-27. -// Copyright 2014 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 -// - -#include "HFMetaEvent.h" - -QSet HFMetaEvent::_types = QSet(); - -QEvent::Type HFMetaEvent::newEventType() { - QEvent::Type newType = static_cast(QEvent::registerEventType()); - _types.insert(newType); - return newType; -} \ No newline at end of file diff --git a/libraries/script-engine/src/HFMetaEvent.h b/libraries/script-engine/src/HFMetaEvent.h deleted file mode 100644 index 2fd71b8a3b..0000000000 --- a/libraries/script-engine/src/HFMetaEvent.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// HFMetaEvent.h -// script-engine/src -// -// Created by Stephen Birarda on 2014-10-27. -// Copyright 2014 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 -// - -#ifndef hifi_HFMetaEvent_h -#define hifi_HFMetaEvent_h - -#include - -class HFMetaEvent : public QEvent { -public: - HFMetaEvent() : QEvent(HFMetaEvent::newEventType()) {}; - HFMetaEvent(QEvent::Type type) : QEvent(type) {}; - static const QSet& types() { return HFMetaEvent::_types; } -protected: - static QEvent::Type newEventType(); - - static QSet _types; -}; - -#endif // hifi_HFMetaEvent_h \ No newline at end of file diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 8ea38f5f13..8c43632456 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -102,8 +102,11 @@ SpatiallyNestablePointer SpatiallyNestable::getParentPointer(bool& success) cons if (parent && parent->getID() == parentID) { // parent pointer is up-to-date if (!_parentKnowsMe) { - parent->beParentOfChild(getThisPointer()); - _parentKnowsMe = true; + SpatialParentTree* parentTree = parent->getParentTree(); + if (!parentTree || parentTree == getParentTree()) { + parent->beParentOfChild(getThisPointer()); + _parentKnowsMe = true; + } } success = true; return parent; @@ -129,8 +132,15 @@ SpatiallyNestablePointer SpatiallyNestable::getParentPointer(bool& success) cons parent = _parent.lock(); if (parent) { - parent->beParentOfChild(getThisPointer()); - _parentKnowsMe = true; + + // it's possible for an entity with a parent of AVATAR_SELF_ID can be imported into a side-tree + // such as the clipboard's. if this is the case, we don't want the parent to consider this a + // child. + SpatialParentTree* parentTree = parent->getParentTree(); + if (!parentTree || parentTree == getParentTree()) { + parent->beParentOfChild(getThisPointer()); + _parentKnowsMe = true; + } } success = (parent || parentID.isNull()); diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 6cf8a927ff..01026ae5ff 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -1018,6 +1018,32 @@ void OffscreenQmlSurface::synthesizeKeyPress(QString key, QObject* targetOverrid } } +static void forEachKeyboard(QQuickItem* item, std::function function) { + QObject* itemObject = item; + while (itemObject) { + if (itemObject->parent()) { + itemObject = itemObject->parent(); + } else { + break; + } + } + + auto keyboards = itemObject->findChildren("keyboard"); + + for (auto keyboardObject : keyboards) { + auto keyboard = qobject_cast(keyboardObject); + if (keyboard == nullptr) { + continue; + } + + if (function) { + function(keyboard); + } + } +} + +static const int TEXTINPUT_PASSWORD = 2; + void OffscreenQmlSurface::setKeyboardRaised(QObject* object, bool raised, bool numeric) { #if Q_OS_ANDROID return; @@ -1030,6 +1056,26 @@ void OffscreenQmlSurface::setKeyboardRaised(QObject* object, bool raised, bool n // if HMD is being worn, allow keyboard to open. allow it to close, HMD or not. if (!raised || qApp->property(hifi::properties::HMD).toBool()) { QQuickItem* item = dynamic_cast(object); + if (!item) { + return; + } + + auto echoMode = item->property("echoMode"); + bool isPasswordField = echoMode.isValid() && echoMode.toInt() == TEXTINPUT_PASSWORD; + + // we need to somehow pass 'isPasswordField' to visible keyboard so it will change its 'mirror text' to asterixes + // the issue in some cases there might be more than one keyboard in object tree and it is hard to understand which one is being used at the moment + // unfortunately attempts to check for visibility failed becuase visibility is not updated yet. So... I don't see other way than just update properties for all the keyboards + forEachKeyboard(item, [&](QQuickItem* keyboard) { + keyboard->setProperty("mirroredText", QVariant::fromValue(QString(""))); + keyboard->setProperty("password", isPasswordField); + }); + + // for future probably makes sense to consider one of the following: + // 1. make keyboard a singleton, which will be dynamically re-parented before showing + // 2. track currently visible keyboard somewhere, allow to subscribe for this signal + // any of above should also eliminate need in duplicated properties and code below + while (item) { // Numeric value may be set in parameter from HTML UI; for QML UI, detect numeric fields here. numeric = numeric || QString(item->metaObject()->className()).left(7) == "SpinBox"; diff --git a/scripts/developer/debugging/debugWindow.js b/scripts/developer/debugging/debugWindow.js index 6dd116089a..b16739b2b8 100644 --- a/scripts/developer/debugging/debugWindow.js +++ b/scripts/developer/debugging/debugWindow.js @@ -53,4 +53,8 @@ ScriptDiscoveryService.clearDebugWindow.connect(function() { window.clearDebugWindow(); }); +Script.scriptEnding.connect(function () { + window.close(); +}) + }()); 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/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index e9e25b058b..d6d80541ee 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -222,7 +222,7 @@ getControllerJointIndex = function (hand) { return controllerJointIndex; } - return MyAvatar.getJointIndex("Head"); + return -1; }; propsArePhysical = function (props) { 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/tests/baking/CMakeLists.txt b/tests/baking/CMakeLists.txt new file mode 100644 index 0000000000..40217eded4 --- /dev/null +++ b/tests/baking/CMakeLists.txt @@ -0,0 +1,10 @@ + +# Declare dependencies +macro (setup_testcase_dependencies) + # link in the shared libraries + link_hifi_libraries(shared baking) + + package_libraries_for_deployment() +endmacro () + +setup_hifi_testcase() diff --git a/tests/baking/src/JSBakerTest.cpp b/tests/baking/src/JSBakerTest.cpp new file mode 100644 index 0000000000..082ffb047f --- /dev/null +++ b/tests/baking/src/JSBakerTest.cpp @@ -0,0 +1,92 @@ +// +// JSBakerTest.cpp +// tests/networking/src +// +// Created by Utkarsh Gautam on 09/26/17. +// Copyright 2015 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 +// + +#include "JSBakerTest.h" +QTEST_MAIN(JSBakerTest) + +void JSBakerTest::setTestCases() { + // Test cases contain a std::pair(input, desiredOutput) + + _testCases.emplace_back("var a=1;", "var a=1;"); + _testCases.emplace_back("var a=1;//single line comment\nvar b=2;", "var a=1;var b=2;"); + _testCases.emplace_back("a\rb", "a\nb"); + _testCases.emplace_back("a/*multi\n line \n comment*/ b", "ab"); + _testCases.emplace_back("a/b", "a/b"); + _testCases.emplace_back("var a = 1;", "var a=1;"); // Multiple spaces omitted + _testCases.emplace_back("var a=\t\t\t1;", "var a=1;"); // Multiple tabs omitted + + // Cases for space not omitted + _testCases.emplace_back("var x", "var x"); + _testCases.emplace_back("a '", "a '"); + _testCases.emplace_back("a $", "a $"); + _testCases.emplace_back("a _", "a _"); + _testCases.emplace_back("a /", "a /"); + _testCases.emplace_back("a 1", "a 1"); + _testCases.emplace_back("1 a", "1 a"); + _testCases.emplace_back("$ a", "$ a"); + _testCases.emplace_back("_ a", "_ a"); + _testCases.emplace_back("/ a", "/ a"); + _testCases.emplace_back("$ $", "$ $"); + _testCases.emplace_back("_ _", "_ _"); + _testCases.emplace_back("/ /", "/ /"); + + _testCases.emplace_back("a\n\n\n\nb", "a\nb"); // Skip multiple new lines + _testCases.emplace_back("a\n\n b", "a\nb"); // Skip multiple new lines followed by whitespace + _testCases.emplace_back("a\n\n b", "a\nb"); // Skip multiple new lines followed by tab + + //Cases for new line not omitted + _testCases.emplace_back("a\nb", "a\nb"); + _testCases.emplace_back("a\n9", "a\n9"); + _testCases.emplace_back("9\na", "9\na"); + _testCases.emplace_back("a\n$", "a\n$"); + _testCases.emplace_back("a\n[", "a\n["); + _testCases.emplace_back("a\n{", "a\n{"); + _testCases.emplace_back("a\n(", "a\n("); + _testCases.emplace_back("a\n+", "a\n+"); + _testCases.emplace_back("a\n'", "a\n'"); + _testCases.emplace_back("a\n-", "a\n-"); + _testCases.emplace_back("$\na", "$\na"); + _testCases.emplace_back("$\na", "$\na"); + _testCases.emplace_back("_\na", "_\na"); + _testCases.emplace_back("]\na", "]\na"); + _testCases.emplace_back("}\na", "}\na"); + _testCases.emplace_back(")\na", ")\na"); + _testCases.emplace_back("+\na", "+\na"); + _testCases.emplace_back("-\na", "-\na"); + + // Cases to check quoted strings are not modified + _testCases.emplace_back("'abcd1234$%^&[](){}'\na", "'abcd1234$%^&[](){}'\na"); + _testCases.emplace_back("\"abcd1234$%^&[](){}\"\na", "\"abcd1234$%^&[](){}\"\na"); + _testCases.emplace_back("`abcd1234$%^&[](){}`\na", "`abcd1234$%^&[](){}`a"); + + // Edge Cases + + //No semicolon to terminate an expression, instead a new line used for termination + _testCases.emplace_back("var x=5\nvar y=6;", "var x=5\nvar y=6;"); + + //a + ++b is minified as a+ ++b. + _testCases.emplace_back("a + ++b", "a + ++b"); + + //a - --b is minified as a- --b. + _testCases.emplace_back("a - --b", "a - --b"); +} + +void JSBakerTest::testJSBaking() { + + for (int i = 0;i < _testCases.size();i++) { + QByteArray output; + auto input = _testCases.at(i).first; + JSBaker::bakeJS(input, output); + + auto desiredOutput = _testCases.at(i).second; + QCOMPARE(output, desiredOutput); + } +} diff --git a/tests/baking/src/JSBakerTest.h b/tests/baking/src/JSBakerTest.h new file mode 100644 index 0000000000..ea2cdc6696 --- /dev/null +++ b/tests/baking/src/JSBakerTest.h @@ -0,0 +1,29 @@ +// +// JSBakerTest.h +// tests/networking/src +// +// Created by Utkarsh Gautam on 9/26/17. +// Copyright 2015 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 +// + +#ifndef hifi_JSBakerTest_h +#define hifi_JSBakerTest_h + +#include +#include + +class JSBakerTest : public QObject { + Q_OBJECT + +private slots: + void setTestCases(); + void testJSBaking(); + +private: + std::vector> _testCases; +}; + +#endif 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;