diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000000..ac639c5ae7 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1 @@ +org.gradle.jvmargs=-Xms2g -Xmx4g diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 4545f48c41..7e1420ef60 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -497,7 +497,7 @@ void AudioMixerClientData::processStreamPacket(ReceivedMessage& message, Concurr if (newStream) { // whenever a stream is added, push it to the concurrent vector of streams added this frame - addedStreams.emplace_back(getNodeID(), getNodeLocalID(), matchingStream->getStreamIdentifier(), matchingStream.get()); + addedStreams.push_back(AddedStream(getNodeID(), getNodeLocalID(), matchingStream->getStreamIdentifier(), matchingStream.get())); } } diff --git a/interface/resources/html/img/tablet-help-keyboard.jpg b/interface/resources/html/img/tablet-help-keyboard.jpg index 7045abed75..f19c399f3a 100644 Binary files a/interface/resources/html/img/tablet-help-keyboard.jpg and b/interface/resources/html/img/tablet-help-keyboard.jpg differ diff --git a/interface/resources/qml/hifi/commerce/common/images/Purchase-First-Run-1.jpg b/interface/resources/qml/hifi/commerce/common/images/Purchase-First-Run-1.jpg index ce0d87363f..5632467c32 100644 Binary files a/interface/resources/qml/hifi/commerce/common/images/Purchase-First-Run-1.jpg and b/interface/resources/qml/hifi/commerce/common/images/Purchase-First-Run-1.jpg differ diff --git a/interface/resources/qml/hifi/commerce/common/images/Purchase-First-Run-2.jpg b/interface/resources/qml/hifi/commerce/common/images/Purchase-First-Run-2.jpg index 40bed974af..38ebf08162 100644 Binary files a/interface/resources/qml/hifi/commerce/common/images/Purchase-First-Run-2.jpg and b/interface/resources/qml/hifi/commerce/common/images/Purchase-First-Run-2.jpg differ diff --git a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml index a515c8031f..0a5c3e8053 100644 --- a/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml +++ b/interface/resources/qml/hifi/commerce/common/sendAsset/SendAsset.qml @@ -39,6 +39,7 @@ Item { property string sendingPubliclyEffectImage; property var http; property var listModelName; + property var keyboardContainer: nil; // This object is always used in a popup or full-screen Wallet section. // This MouseArea is used to prevent a user from being @@ -1125,8 +1126,7 @@ Item { checked: Settings.getValue("sendAssetsNearbyPublicly", true); text: "Show Effect" // Anchors - anchors.top: messageContainer.bottom; - anchors.topMargin: 16; + anchors.verticalCenter: bottomBarContainer.verticalCenter; anchors.left: parent.left; anchors.leftMargin: 20; width: 130; @@ -1168,6 +1168,9 @@ Item { lightboxPopup.visible = false; } lightboxPopup.visible = true; + if (keyboardContainer) { + keyboardContainer.keyboardRaised = false; + } } } } @@ -1178,8 +1181,8 @@ Item { anchors.leftMargin: 20; anchors.right: parent.right; anchors.rightMargin: 20; - anchors.bottom: parent.bottom; - anchors.bottomMargin: 20; + anchors.top: messageContainer.bottom; + anchors.topMargin: 20; height: 60; // "CANCEL" button @@ -1187,11 +1190,11 @@ Item { id: cancelButton_sendAssetStep; color: root.assetName === "" ? hifi.buttons.noneBorderlessWhite : hifi.buttons.noneBorderlessGray; colorScheme: hifi.colorSchemes.dark; - anchors.left: parent.left; - anchors.leftMargin: 24; + anchors.right: sendButton.left; + anchors.rightMargin: 24; anchors.verticalCenter: parent.verticalCenter; height: 40; - width: 150; + width: 100; text: "CANCEL"; onClicked: { resetSendAssetData(); @@ -1205,10 +1208,10 @@ Item { color: hifi.buttons.blue; colorScheme: root.assetName === "" ? hifi.colorSchemes.dark : hifi.colorSchemes.light; anchors.right: parent.right; - anchors.rightMargin: 24; + anchors.rightMargin: 0; anchors.verticalCenter: parent.verticalCenter; height: 40; - width: 150; + width: 100; text: "SUBMIT"; onClicked: { if (root.assetName === "" && parseInt(amountTextField.text) > parseInt(balanceText.text)) { diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 2435678e77..015ec3a172 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -158,6 +158,7 @@ Rectangle { listModelName: "Gift Connections"; z: 998; visible: root.activeView === "giftAsset"; + keyboardContainer: root; anchors.fill: parent; parentAppTitleBarHeight: 70; parentAppNavBarHeight: 0; @@ -585,7 +586,7 @@ Rectangle { visible: purchasesModel.count !== 0; clip: true; model: purchasesModel; - snapMode: ListView.SnapToItem; + snapMode: ListView.NoSnap; // Anchors anchors.top: separator.bottom; anchors.left: parent.left; diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 588b80c435..cbb77883df 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -354,6 +354,7 @@ Rectangle { listModelName: "Send Money Connections"; z: 997; visible: root.activeView === "sendMoney"; + keyboardContainer: root; anchors.fill: parent; parentAppTitleBarHeight: titleBarContainer.height; parentAppNavBarHeight: tabButtonsContainer.height; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2dc3645be0..1bd6af2eba 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -8476,6 +8476,16 @@ QUuid Application::getTabletFrameID() const { return HMD->getCurrentTabletFrameID(); } +QVector Application::getTabletIDs() const { + // Most important overlays first. + QVector result; + auto HMD = DependencyManager::get(); + result << HMD->getCurrentTabletScreenID(); + result << HMD->getCurrentHomeButtonID(); + result << HMD->getCurrentTabletFrameID(); + return result; +} + void Application::setAvatarOverrideUrl(const QUrl& url, bool save) { _avatarOverrideUrl = url; _saveAvatarOverrideUrl = save; diff --git a/interface/src/Application.h b/interface/src/Application.h index 75260b910f..c75c2e9efc 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -298,6 +298,7 @@ public: OverlayID getTabletScreenID() const; OverlayID getTabletHomeButtonID() const; QUuid getTabletFrameID() const; // may be an entity or an overlay + QVector getTabletIDs() const; // In order of most important IDs first. void setAvatarOverrideUrl(const QUrl& url, bool save); void clearAvatarOverrideUrl() { _avatarOverrideUrl = QUrl(); _saveAvatarOverrideUrl = false; } diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index 5c79bedc9a..ce21c68c3d 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -78,8 +78,10 @@ void addAvatarEntities(const QVariantList& avatarEntities) { } entity->setLastBroadcast(usecTimestampNow()); - // since we're creating this object we will immediately volunteer to own its simulation - entity->setScriptSimulationPriority(VOLUNTEER_SIMULATION_PRIORITY); + if (entityProperties.getDynamic()) { + // since we're creating a dynamic object we volunteer immediately to own its simulation + entity->upgradeScriptSimulationPriority(VOLUNTEER_SIMULATION_PRIORITY); + } entityProperties.setLastEdited(entity->getLastEdited()); } else { qCDebug(entities) << "AvatarEntitiesBookmark failed to add new Entity to local Octree"; @@ -108,6 +110,9 @@ AvatarBookmarks::AvatarBookmarks() { if (!QFile::copy(defaultBookmarksFilename, _bookmarksFilename)) { qDebug() << "failed to copy" << defaultBookmarksFilename << "to" << _bookmarksFilename; + } else { + QFile bookmarksFile(_bookmarksFilename); + bookmarksFile.setPermissions(bookmarksFile.permissions() | QFile::WriteUser); } } readFromFile(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c61b424bce..a7c8c3e179 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -122,7 +122,6 @@ MyAvatar::MyAvatar(QThread* thread) : _goToOrientation(), _prevShouldDrawHead(true), _audioListenerMode(FROM_HEAD), - _hmdAtRestDetector(glm::vec3(0), glm::quat()), _dominantHandSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "dominantHand", DOMINANT_RIGHT_HAND), _headPitchSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "", 0.0f), _scaleSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "scale", _targetScale), @@ -702,6 +701,46 @@ void MyAvatar::simulate(float deltaTime) { // before we perform rig animations and IK. updateSensorToWorldMatrix(); + // if we detect the hand controller is at rest, i.e. lying on the table, or the hand is too far away from the hmd + // disable the associated hand controller input. + { + // NOTE: all poses are in sensor space. + auto leftHandIter = _controllerPoseMap.find(controller::Action::LEFT_HAND); + if (leftHandIter != _controllerPoseMap.end() && leftHandIter->second.isValid()) { + _leftHandAtRestDetector.update(leftHandIter->second.getTranslation(), leftHandIter->second.getRotation()); + if (_leftHandAtRestDetector.isAtRest()) { + leftHandIter->second.valid = false; + } + } else { + _leftHandAtRestDetector.invalidate(); + } + + auto rightHandIter = _controllerPoseMap.find(controller::Action::RIGHT_HAND); + if (rightHandIter != _controllerPoseMap.end() && rightHandIter->second.isValid()) { + _rightHandAtRestDetector.update(rightHandIter->second.getTranslation(), rightHandIter->second.getRotation()); + if (_rightHandAtRestDetector.isAtRest()) { + rightHandIter->second.valid = false; + } + } else { + _rightHandAtRestDetector.invalidate(); + } + + auto headIter = _controllerPoseMap.find(controller::Action::HEAD); + + // The 99th percentile man has a spine to fingertip to height ratio of 0.45. Lets increase that by about 10% to 0.5 + // then measure the distance the center of the eyes to the finger tips. To come up with this ratio. + // From "The Measure of Man and Woman: Human Factors in Design, Revised Edition" by Alvin R. Tilley, Henry Dreyfuss Associates + const float MAX_HEAD_TO_HAND_DISTANCE_RATIO = 0.52f; + + float maxHeadHandDistance = getUserHeight() * MAX_HEAD_TO_HAND_DISTANCE_RATIO; + if (glm::length(headIter->second.getTranslation() - leftHandIter->second.getTranslation()) > maxHeadHandDistance) { + leftHandIter->second.valid = false; + } + if (glm::length(headIter->second.getTranslation() - rightHandIter->second.getTranslation()) > maxHeadHandDistance) { + rightHandIter->second.valid = false; + } + } + { PerformanceTimer perfTimer("skeleton"); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 16b765711a..08a7c09fa4 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -629,8 +629,6 @@ public: const MyHead* getMyHead() const; - Q_INVOKABLE void toggleSmoothPoleVectors() { _skeletonModel->getRig().toggleSmoothPoleVectors(); }; - /**jsdoc * Get the current position of the avatar's "Head" joint. * @function MyAvatar.getHeadPosition @@ -951,50 +949,72 @@ public: void removeWearableAvatarEntities(); /**jsdoc + * Check whether your avatar is flying or not. * @function MyAvatar.isFlying - * @returns {boolean} + * @returns {boolean} true if your avatar is flying and not taking off or falling, otherwise + * false. */ Q_INVOKABLE bool isFlying(); /**jsdoc + * Check whether your avatar is in the air or not. * @function MyAvatar.isInAir - * @returns {boolean} + * @returns {boolean} true if your avatar is taking off, flying, or falling, otherwise false + * because your avatar is on the ground. */ Q_INVOKABLE bool isInAir(); /**jsdoc + * Set your preference for flying in your current desktop or HMD display mode. Note that your ability to fly also depends + * on whether the domain you're in allows you to fly. * @function MyAvatar.setFlyingEnabled - * @param {boolean} enabled + * @param {boolean} enabled - Set true if you want to enable flying in your current desktop or HMD display + * mode, otherwise set false. */ Q_INVOKABLE void setFlyingEnabled(bool enabled); /**jsdoc + * Get your preference for flying in your current desktop or HMD display mode. Note that your ability to fly also depends + * on whether the domain you're in allows you to fly. * @function MyAvatar.getFlyingEnabled - * @returns {boolean} + * @returns {boolean} true if your preference is to enable flying in your current desktop or HMD display mode, + * otherwise false. */ Q_INVOKABLE bool getFlyingEnabled(); /**jsdoc + * Set your preference for flying in desktop display mode. Note that your ability to fly also depends on whether the domain + * you're in allows you to fly. * @function MyAvatar.setFlyingDesktopPref - * @param {boolean} enabled + * @param {boolean} enabled - Set true if you want to enable flying in desktop display mode, otherwise set + * false. */ Q_INVOKABLE void setFlyingDesktopPref(bool enabled); /**jsdoc + * Get your preference for flying in desktop display mode. Note that your ability to fly also depends on whether the domain + * you're in allows you to fly. * @function MyAvatar.getFlyingDesktopPref - * @returns {boolean} + * @returns {boolean} true if your preference is to enable flying in desktop display mode, otherwise + * false. */ Q_INVOKABLE bool getFlyingDesktopPref(); /**jsdoc - * @function MyAvatar.setFlyingDesktopPref - * @param {boolean} enabled + * Set your preference for flying in HMD display mode. Note that your ability to fly also depends on whether the domain + * you're in allows you to fly. + * @function MyAvatar.setFlyingHMDPref + * @param {boolean} enabled - Set true if you want to enable flying in HMD display mode, otherwise set + * false. */ Q_INVOKABLE void setFlyingHMDPref(bool enabled); /**jsdoc - * @function MyAvatar.getFlyingDesktopPref - * @returns {boolean} + * Get your preference for flying in HMD display mode. Note that your ability to fly also depends on whether the domain + * you're in allows you to fly. + * @function MyAvatar.getFlyingHMDPref + * @returns {boolean} true if your preference is to enable flying in HMD display mode, otherwise + * false. */ Q_INVOKABLE bool getFlyingHMDPref(); @@ -1766,8 +1786,8 @@ private: glm::vec3 _customListenPosition; glm::quat _customListenOrientation; - AtRestDetector _hmdAtRestDetector; - bool _lastIsMoving { false }; + AtRestDetector _leftHandAtRestDetector; + AtRestDetector _rightHandAtRestDetector; // all poses are in sensor-frame std::map _controllerPoseMap; diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 3084542472..c1a49d7a10 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -46,7 +46,7 @@ static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) { } glm::mat4 hipsMat; - if (myAvatar->getCenterOfGravityModelEnabled() && !isFlying && !(myAvatar->getIsInWalkingState())) { + if (myAvatar->getCenterOfGravityModelEnabled() && !isFlying && !(myAvatar->getIsInWalkingState()) && myAvatar->getHMDLeanRecenterEnabled()) { // then we use center of gravity model hipsMat = myAvatar->deriveBodyUsingCgModel(); } else { diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp index 625998eb95..c2687fd525 100644 --- a/interface/src/avatar/OtherAvatar.cpp +++ b/interface/src/avatar/OtherAvatar.cpp @@ -11,12 +11,12 @@ #include "AvatarMotionState.h" -static xColor getLoadingOrbColor(Avatar::LoadingStatus loadingStatus) { +static glm::u8vec3 getLoadingOrbColor(Avatar::LoadingStatus loadingStatus) { - const xColor NO_MODEL_COLOR(0xe3, 0xe3, 0xe3); - const xColor LOAD_MODEL_COLOR(0xef, 0x93, 0xd1); - const xColor LOAD_SUCCESS_COLOR(0x1f, 0xc6, 0xa6); - const xColor LOAD_FAILURE_COLOR(0xc6, 0x21, 0x47); + const glm::u8vec3 NO_MODEL_COLOR(0xe3, 0xe3, 0xe3); + const glm::u8vec3 LOAD_MODEL_COLOR(0xef, 0x93, 0xd1); + const glm::u8vec3 LOAD_SUCCESS_COLOR(0x1f, 0xc6, 0xa6); + const glm::u8vec3 LOAD_FAILURE_COLOR(0xc6, 0x21, 0x47); switch (loadingStatus) { case Avatar::LoadingStatus::NoModel: return NO_MODEL_COLOR; diff --git a/interface/src/raypick/ParabolaPointer.cpp b/interface/src/raypick/ParabolaPointer.cpp index dde0daf0df..e45b11b479 100644 --- a/interface/src/raypick/ParabolaPointer.cpp +++ b/interface/src/raypick/ParabolaPointer.cpp @@ -52,8 +52,7 @@ void ParabolaPointer::editRenderStatePath(const std::string& state, const QVaria if (!pathMap.isEmpty()) { enabled = true; if (pathMap["color"].isValid()) { - bool valid; - color = toGlm(xColorFromVariant(pathMap["color"], valid)); + color = toGlm(u8vec3FromVariant(pathMap["color"])); } if (pathMap["alpha"].isValid()) { alpha = pathMap["alpha"].toFloat(); @@ -250,8 +249,7 @@ std::shared_ptr ParabolaPointer::buildRenderState(const QVa enabled = true; QVariantMap pathMap = propMap["path"].toMap(); if (pathMap["color"].isValid()) { - bool valid; - color = toGlm(xColorFromVariant(pathMap["color"], valid)); + color = toGlm(u8vec3FromVariant(pathMap["color"])); } if (pathMap["alpha"].isValid()) { diff --git a/interface/src/scripting/SelectionScriptingInterface.cpp b/interface/src/scripting/SelectionScriptingInterface.cpp index 9716b7e665..4a8a72b16d 100644 --- a/interface/src/scripting/SelectionScriptingInterface.cpp +++ b/interface/src/scripting/SelectionScriptingInterface.cpp @@ -421,7 +421,7 @@ bool SelectionHighlightStyle::fromVariantMap(const QVariantMap& properties) { auto colorVariant = properties["outlineUnoccludedColor"]; if (colorVariant.isValid()) { bool isValid; - auto color = xColorFromVariant(colorVariant, isValid); + auto color = u8vec3FromVariant(colorVariant, isValid); if (isValid) { _style._outlineUnoccluded.color = toGlm(color); } @@ -429,7 +429,7 @@ bool SelectionHighlightStyle::fromVariantMap(const QVariantMap& properties) { colorVariant = properties["outlineOccludedColor"]; if (colorVariant.isValid()) { bool isValid; - auto color = xColorFromVariant(colorVariant, isValid); + auto color = u8vec3FromVariant(colorVariant, isValid); if (isValid) { _style._outlineOccluded.color = toGlm(color); } @@ -437,7 +437,7 @@ bool SelectionHighlightStyle::fromVariantMap(const QVariantMap& properties) { colorVariant = properties["fillUnoccludedColor"]; if (colorVariant.isValid()) { bool isValid; - auto color = xColorFromVariant(colorVariant, isValid); + auto color = u8vec3FromVariant(colorVariant, isValid); if (isValid) { _style._fillUnoccluded.color = toGlm(color); } @@ -445,7 +445,7 @@ bool SelectionHighlightStyle::fromVariantMap(const QVariantMap& properties) { colorVariant = properties["fillOccludedColor"]; if (colorVariant.isValid()) { bool isValid; - auto color = xColorFromVariant(colorVariant, isValid); + auto color = u8vec3FromVariant(colorVariant, isValid); if (isValid) { _style._fillOccluded.color = toGlm(color); } @@ -497,10 +497,11 @@ bool SelectionHighlightStyle::fromVariantMap(const QVariantMap& properties) { QVariantMap SelectionHighlightStyle::toVariantMap() const { QVariantMap properties; - properties["outlineUnoccludedColor"] = xColorToVariant(xColorFromGlm(_style._outlineUnoccluded.color)); - properties["outlineOccludedColor"] = xColorToVariant(xColorFromGlm(_style._outlineOccluded.color)); - properties["fillUnoccludedColor"] = xColorToVariant(xColorFromGlm(_style._fillUnoccluded.color)); - properties["fillOccludedColor"] = xColorToVariant(xColorFromGlm(_style._fillOccluded.color)); + const float MAX_COLOR = 255.0f; + properties["outlineUnoccludedColor"] = u8vec3ColortoVariant(_style._outlineUnoccluded.color * MAX_COLOR); + properties["outlineOccludedColor"] = u8vec3ColortoVariant(_style._outlineOccluded.color * MAX_COLOR); + properties["fillUnoccludedColor"] = u8vec3ColortoVariant(_style._fillUnoccluded.color * MAX_COLOR); + properties["fillOccludedColor"] = u8vec3ColortoVariant(_style._fillOccluded.color * MAX_COLOR); properties["outlineUnoccludedAlpha"] = _style._outlineUnoccluded.alpha; properties["outlineOccludedAlpha"] = _style._outlineOccluded.alpha; diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index 1d8db69e26..14c1f1a15a 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -191,8 +191,6 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { if (properties["parentID"].isValid()) { setParentID(QUuid(properties["parentID"].toString())); - bool success; - getParentPointer(success); // call this to hook-up the parent's back-pointers to its child overlays needRenderItemUpdate = true; } if (properties["parentJointIndex"].isValid()) { diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index 1d418d966e..a98ea7070e 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -75,7 +75,6 @@ void Circle3DOverlay::render(RenderArgs* args) { const float FULL_CIRCLE = 360.0f; const float SLICES = 180.0f; // The amount of segment to create the circle const float SLICE_ANGLE_RADIANS = glm::radians(FULL_CIRCLE / SLICES); - const float MAX_COLOR = 255.0f; auto geometryCache = DependencyManager::get(); @@ -246,20 +245,15 @@ void Circle3DOverlay::render(RenderArgs* args) { angle += tickMarkAngle; } } - - xColor majorColorX = getMajorTickMarksColor(); - glm::vec4 majorColor(majorColorX.red / MAX_COLOR, majorColorX.green / MAX_COLOR, majorColorX.blue / MAX_COLOR, alpha); - + + glm::vec4 majorColor(toGlm(getMajorTickMarksColor()), alpha); geometryCache->updateVertices(_majorTicksVerticesID, majorPoints, majorColor); - - xColor minorColorX = getMinorTickMarksColor(); - glm::vec4 minorColor(minorColorX.red / MAX_COLOR, minorColorX.green / MAX_COLOR, minorColorX.blue / MAX_COLOR, alpha); - + glm::vec4 minorColor(toGlm(getMinorTickMarksColor()), alpha); geometryCache->updateVertices(_minorTicksVerticesID, minorPoints, minorColor); } - + geometryCache->renderVertices(batch, gpu::LINES, _majorTicksVerticesID); - + geometryCache->renderVertices(batch, gpu::LINES, _minorTicksVerticesID); } } @@ -280,8 +274,8 @@ template T fromVariant(const QVariant& v, bool& valid) { return qvariant_cast(v); } -template<> xColor fromVariant(const QVariant& v, bool& valid) { - return xColorFromVariant(v, valid); +template<> glm::u8vec3 fromVariant(const QVariant& v, bool& valid) { + return u8vec3FromVariant(v, valid); } template @@ -344,11 +338,11 @@ void Circle3DOverlay::setProperties(const QVariantMap& properties) { _dirty |= updateIfValid(properties, "outerStartAlpha", _outerStartAlpha); _dirty |= updateIfValid(properties, "outerEndAlpha", _outerEndAlpha); - _dirty |= updateIfValid(properties, "color", { _innerStartColor, _innerEndColor, _outerStartColor, _outerEndColor }); - _dirty |= updateIfValid(properties, "startColor", { _innerStartColor, _outerStartColor } ); - _dirty |= updateIfValid(properties, "endColor", { _innerEndColor, _outerEndColor } ); - _dirty |= updateIfValid(properties, "innerColor", { _innerStartColor, _innerEndColor } ); - _dirty |= updateIfValid(properties, "outerColor", { _outerStartColor, _outerEndColor } ); + _dirty |= updateIfValid(properties, "color", { _innerStartColor, _innerEndColor, _outerStartColor, _outerEndColor }); + _dirty |= updateIfValid(properties, "startColor", { _innerStartColor, _outerStartColor } ); + _dirty |= updateIfValid(properties, "endColor", { _innerEndColor, _outerEndColor } ); + _dirty |= updateIfValid(properties, "innerColor", { _innerStartColor, _innerEndColor } ); + _dirty |= updateIfValid(properties, "outerColor", { _outerStartColor, _outerEndColor } ); _dirty |= updateIfValid(properties, "innerStartColor", _innerStartColor); _dirty |= updateIfValid(properties, "innerEndColor", _innerEndColor); _dirty |= updateIfValid(properties, "outerStartColor", _outerStartColor); @@ -421,7 +415,7 @@ void Circle3DOverlay::setProperties(const QVariantMap& properties) { * @property {number} endAt=360 - The counter-clockwise angle from the overlay's x-axis that drawing ends at, in degrees. * @property {number} outerRadius=1 - The outer radius of the overlay, in meters. Synonym: radius. * @property {number} innerRadius=0 - The inner radius of the overlay, in meters. - * @property {Color} color=255,255,255 - The color of the overlay. Setting this value also sets the values of + * @property {Color} color=255,255,255 - The color of the overlay. Setting this value also sets the values of * innerStartColor, innerEndColor, outerStartColor, and outerEndColor. * @property {Color} startColor - Sets the values of innerStartColor and outerStartColor. * Write-only. @@ -478,16 +472,16 @@ QVariant Circle3DOverlay::getProperty(const QString& property) { return _innerRadius; } if (property == "innerStartColor") { - return xColorToVariant(_innerStartColor); + return u8vec3ColortoVariant(_innerStartColor); } if (property == "innerEndColor") { - return xColorToVariant(_innerEndColor); + return u8vec3ColortoVariant(_innerEndColor); } if (property == "outerStartColor") { - return xColorToVariant(_outerStartColor); + return u8vec3ColortoVariant(_outerStartColor); } if (property == "outerEndColor") { - return xColorToVariant(_outerEndColor); + return u8vec3ColortoVariant(_outerEndColor); } if (property == "innerStartAlpha") { return _innerStartAlpha; @@ -517,10 +511,10 @@ QVariant Circle3DOverlay::getProperty(const QString& property) { return _minorTickMarksLength; } if (property == "majorTickMarksColor") { - return xColorToVariant(_majorTickMarksColor); + return u8vec3ColortoVariant(_majorTickMarksColor); } if (property == "minorTickMarksColor") { - return xColorToVariant(_minorTickMarksColor); + return u8vec3ColortoVariant(_minorTickMarksColor); } return Planar3DOverlay::getProperty(property); diff --git a/interface/src/ui/overlays/Circle3DOverlay.h b/interface/src/ui/overlays/Circle3DOverlay.h index b3fa24fb16..ca5e05a53b 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.h +++ b/interface/src/ui/overlays/Circle3DOverlay.h @@ -39,8 +39,8 @@ public: float getMinorTickMarksAngle() const { return _minorTickMarksAngle; } float getMajorTickMarksLength() const { return _majorTickMarksLength; } float getMinorTickMarksLength() const { return _minorTickMarksLength; } - xColor getMajorTickMarksColor() const { return _majorTickMarksColor; } - xColor getMinorTickMarksColor() const { return _minorTickMarksColor; } + glm::u8vec3 getMajorTickMarksColor() const { return _majorTickMarksColor; } + glm::u8vec3 getMinorTickMarksColor() const { return _minorTickMarksColor; } void setStartAt(float value) { _startAt = value; } void setEndAt(float value) { _endAt = value; } @@ -51,8 +51,8 @@ public: void setMinorTickMarksAngle(float value) { _minorTickMarksAngle = value; } void setMajorTickMarksLength(float value) { _majorTickMarksLength = value; } void setMinorTickMarksLength(float value) { _minorTickMarksLength = value; } - void setMajorTickMarksColor(const xColor& value) { _majorTickMarksColor = value; } - void setMinorTickMarksColor(const xColor& value) { _minorTickMarksColor = value; } + void setMajorTickMarksColor(const glm::u8vec3& value) { _majorTickMarksColor = value; } + void setMinorTickMarksColor(const glm::u8vec3& value) { _minorTickMarksColor = value; } virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precisionPicking = false) override; @@ -67,10 +67,10 @@ protected: float _outerRadius { 1 }; float _innerRadius { 0 }; - xColor _innerStartColor { DEFAULT_OVERLAY_COLOR }; - xColor _innerEndColor { DEFAULT_OVERLAY_COLOR }; - xColor _outerStartColor { DEFAULT_OVERLAY_COLOR }; - xColor _outerEndColor { DEFAULT_OVERLAY_COLOR }; + glm::u8vec3 _innerStartColor { DEFAULT_OVERLAY_COLOR }; + glm::u8vec3 _innerEndColor { DEFAULT_OVERLAY_COLOR }; + glm::u8vec3 _outerStartColor { DEFAULT_OVERLAY_COLOR }; + glm::u8vec3 _outerEndColor { DEFAULT_OVERLAY_COLOR }; float _innerStartAlpha { DEFAULT_ALPHA }; float _innerEndAlpha { DEFAULT_ALPHA }; float _outerStartAlpha { DEFAULT_ALPHA }; @@ -81,8 +81,8 @@ protected: float _minorTickMarksAngle { 0 }; float _majorTickMarksLength { 0 }; float _minorTickMarksLength { 0 }; - xColor _majorTickMarksColor { DEFAULT_OVERLAY_COLOR }; - xColor _minorTickMarksColor { DEFAULT_OVERLAY_COLOR }; + glm::u8vec3 _majorTickMarksColor { DEFAULT_OVERLAY_COLOR }; + glm::u8vec3 _minorTickMarksColor { DEFAULT_OVERLAY_COLOR }; gpu::Primitive _solidPrimitive { gpu::TRIANGLE_FAN }; int _quadVerticesID { 0 }; int _lineVerticesID { 0 }; diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 5de8bb1a2a..9f1018f6c7 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -83,7 +83,7 @@ ContextOverlayInterface::ContextOverlayInterface() { _challengeOwnershipTimeoutTimer.setSingleShot(true); } -static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 }; +static const glm::u8vec3 CONTEXT_OVERLAY_COLOR = { 255, 255, 255 }; static const float CONTEXT_OVERLAY_INSIDE_DISTANCE = 1.0f; // in meters static const float CONTEXT_OVERLAY_SIZE = 0.09f; // in meters, same x and y dims static const float CONTEXT_OVERLAY_OFFSET_DISTANCE = 0.1f; @@ -142,14 +142,15 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec3 cameraPosition = qApp->getCamera().getPosition(); glm::vec3 entityDimensions = entityProperties.getDimensions(); glm::vec3 entityPosition = entityProperties.getPosition(); + glm::vec3 registrationPoint = entityProperties.getRegistrationPoint(); glm::vec3 contextOverlayPosition = entityProperties.getPosition(); glm::vec2 contextOverlayDimensions; // Update the position of the overlay if the registration point of the entity // isn't default - if (entityProperties.getRegistrationPoint() != glm::vec3(0.5f)) { - glm::vec3 adjustPos = entityProperties.getRegistrationPoint() - glm::vec3(0.5f); - entityPosition = entityPosition - (entityProperties.getRotation() * (adjustPos * entityProperties.getDimensions())); + if (registrationPoint != glm::vec3(0.5f)) { + glm::vec3 adjustPos = registrationPoint - glm::vec3(0.5f); + entityPosition = entityPosition - (entityProperties.getRotation() * (adjustPos * entityDimensions)); } enableEntityHighlight(entityItemID); diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index 38fff5f26f..581db672a3 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -49,11 +49,8 @@ void Cube3DOverlay::render(RenderArgs* args) { } float alpha = getAlpha(); - xColor color = getColor(); - const float MAX_COLOR = 255.0f; - glm::vec4 cubeColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); - - + glm::u8vec3 color = getColor(); + glm::vec4 cubeColor(toGlm(color), alpha); auto batch = args->_batch; if (batch) { diff --git a/interface/src/ui/overlays/Grid3DOverlay.cpp b/interface/src/ui/overlays/Grid3DOverlay.cpp index 15eb9eef76..87ab0fb2e8 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.cpp +++ b/interface/src/ui/overlays/Grid3DOverlay.cpp @@ -57,11 +57,9 @@ void Grid3DOverlay::render(RenderArgs* args) { return; // do nothing if we're not visible } - const float MAX_COLOR = 255.0f; - float alpha = getAlpha(); - xColor color = getColor(); - glm::vec4 gridColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); + glm::u8vec3 color = getColor(); + glm::vec4 gridColor(toGlm(color), alpha); auto batch = args->_batch; diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index 608e7eb72f..18da15f019 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -107,17 +107,16 @@ void Image3DOverlay::render(RenderArgs* args) { glm::vec2 texCoordBottomRight((fromImage.x() + fromImage.width() - 0.5f) / imageWidth, (fromImage.y() + fromImage.height() - 0.5f) / imageHeight); - const float MAX_COLOR = 255.0f; - xColor color = getColor(); float alpha = getAlpha(); + glm::u8vec3 color = getColor(); + glm::vec4 imageColor(toGlm(color), alpha); batch->setModelTransform(getRenderTransform()); batch->setResourceTexture(0, _texture->getGPUTexture()); DependencyManager::get()->renderQuad( *batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, - glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha), - _geometryId + imageColor, _geometryId ); batch->setResourceTexture(0, nullptr); // restore default white color after me diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index af6c3c2472..e6546686b0 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -128,9 +128,8 @@ void Line3DOverlay::render(RenderArgs* args) { } float alpha = getAlpha(); - xColor color = getColor(); - const float MAX_COLOR = 255.0f; - glm::vec4 colorv4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); + glm::u8vec3 color = getColor(); + glm::vec4 colorv4(toGlm(color), alpha); auto batch = args->_batch; if (batch) { batch->setModelTransform(Transform()); diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index faa15ee2b4..22cf924727 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -15,7 +15,7 @@ #include "Application.h" -const xColor Overlay::DEFAULT_OVERLAY_COLOR = { 255, 255, 255 }; +const glm::u8vec3 Overlay::DEFAULT_OVERLAY_COLOR = { 255, 255, 255 }; const float Overlay::DEFAULT_ALPHA = 0.7f; Overlay::Overlay() : @@ -57,7 +57,7 @@ Overlay::~Overlay() { void Overlay::setProperties(const QVariantMap& properties) { bool valid; - auto color = xColorFromVariant(properties["color"], valid); + auto color = u8vec3FromVariant(properties["color"], valid); if (valid) { _color = color; } @@ -116,7 +116,7 @@ QVariant Overlay::getProperty(const QString& property) { return QVariant(getType()); } if (property == "color") { - return xColorToVariant(_color); + return u8vec3ColortoVariant(_color); } if (property == "alpha") { return _alpha; @@ -143,21 +143,21 @@ QVariant Overlay::getProperty(const QString& property) { return QVariant(); } -xColor Overlay::getColor() { +glm::u8vec3 Overlay::getColor() { if (_colorPulse == 0.0f) { return _color; } float pulseLevel = updatePulse(); - xColor result = _color; + glm::u8vec3 result = _color; if (_colorPulse < 0.0f) { - result.red *= (1.0f - pulseLevel); - result.green *= (1.0f - pulseLevel); - result.blue *= (1.0f - pulseLevel); + result.x *= (1.0f - pulseLevel); + result.y *= (1.0f - pulseLevel); + result.z *= (1.0f - pulseLevel); } else { - result.red *= pulseLevel; - result.green *= pulseLevel; - result.blue *= pulseLevel; + result.x *= pulseLevel; + result.y *= pulseLevel; + result.z *= pulseLevel; } return result; } diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index 45fc77a452..8e430f7e85 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -11,8 +11,6 @@ #ifndef hifi_Overlay_h #define hifi_Overlay_h -// include this before QGLWidget, which includes an earlier version of OpenGL -#include // for xColor #include class OverlayID : public QUuid { @@ -59,7 +57,7 @@ public: virtual bool isTransparent() { return getAlphaPulse() != 0.0f || getAlpha() != 1.0f; }; virtual bool getIsVisibleInSecondaryCamera() const { return false; } - xColor getColor(); + glm::u8vec3 getColor(); float getAlpha(); float getPulseMax() const { return _pulseMax; } @@ -73,7 +71,7 @@ public: // setters virtual void setVisible(bool visible) { _visible = visible; } void setDrawHUDLayer(bool drawHUDLayer); - void setColor(const xColor& color) { _color = color; } + void setColor(const glm::u8vec3& color) { _color = color; } void setAlpha(float alpha) { _alpha = alpha; } void setPulseMax(float value) { _pulseMax = value; } @@ -115,12 +113,12 @@ protected: float _alphaPulse; // ratio of the pulse to the alpha float _colorPulse; // ratio of the pulse to the color - xColor _color; + glm::u8vec3 _color; bool _visible; // should the overlay be drawn at all unsigned int _stackOrder { 0 }; - static const xColor DEFAULT_OVERLAY_COLOR; + static const glm::u8vec3 DEFAULT_OVERLAY_COLOR; static const float DEFAULT_ALPHA; std::unordered_map _materials; diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index de4ff94719..f4c98bb9e0 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -532,6 +532,8 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionVector(const PickRay bool visibleOnly, bool collidableOnly) { float bestDistance = std::numeric_limits::max(); bool bestIsFront = false; + bool bestIsTablet = false; + auto tabletIDs = qApp->getTabletIDs(); QMutexLocker locker(&_mutex); RayToOverlayIntersectionResult result; @@ -554,10 +556,11 @@ RayToOverlayIntersectionResult Overlays::findRayIntersectionVector(const PickRay if (thisOverlay->findRayIntersectionExtraInfo(ray.origin, ray.direction, thisDistance, thisFace, thisSurfaceNormal, thisExtraInfo, precisionPicking)) { bool isDrawInFront = thisOverlay->getDrawInFront(); - if ((bestIsFront && isDrawInFront && thisDistance < bestDistance) - || (!bestIsFront && (isDrawInFront || thisDistance < bestDistance))) { - + bool isTablet = tabletIDs.contains(thisID); + if ((isDrawInFront && !bestIsFront && !bestIsTablet) + || ((isTablet || isDrawInFront || !bestIsFront) && thisDistance < bestDistance)) { bestIsFront = isDrawInFront; + bestIsTablet = isTablet; bestDistance = thisDistance; result.intersects = true; result.distance = thisDistance; @@ -629,9 +632,9 @@ QScriptValue RayToOverlayIntersectionResultToScriptValue(QScriptEngine* engine, obj.setProperty("distance", value.distance); obj.setProperty("face", boxFaceToString(value.face)); - QScriptValue intersection = vec3toScriptValue(engine, value.intersection); + QScriptValue intersection = vec3ToScriptValue(engine, value.intersection); obj.setProperty("intersection", intersection); - QScriptValue surfaceNormal = vec3toScriptValue(engine, value.surfaceNormal); + QScriptValue surfaceNormal = vec3ToScriptValue(engine, value.surfaceNormal); obj.setProperty("surfaceNormal", surfaceNormal); obj.setProperty("extraInfo", engine->toScriptValue(value.extraInfo)); return obj; @@ -828,40 +831,12 @@ PointerEvent Overlays::calculateOverlayPointerEvent(OverlayID overlayID, PickRay } -RayToOverlayIntersectionResult Overlays::findRayIntersectionForMouseEvent(PickRay ray) { - QVector overlaysToInclude; - QVector overlaysToDiscard; - RayToOverlayIntersectionResult rayPickResult; - - // first priority is tablet screen - overlaysToInclude << qApp->getTabletScreenID(); - rayPickResult = findRayIntersectionVector(ray, true, overlaysToInclude, overlaysToDiscard); - if (rayPickResult.intersects) { - return rayPickResult; - } - // then tablet home button - overlaysToInclude.clear(); - overlaysToInclude << qApp->getTabletHomeButtonID(); - rayPickResult = findRayIntersectionVector(ray, true, overlaysToInclude, overlaysToDiscard); - if (rayPickResult.intersects) { - return rayPickResult; - } - // then tablet frame - overlaysToInclude.clear(); - overlaysToInclude << OverlayID(qApp->getTabletFrameID()); - rayPickResult = findRayIntersectionVector(ray, true, overlaysToInclude, overlaysToDiscard); - if (rayPickResult.intersects) { - return rayPickResult; - } - // then whatever - return findRayIntersection(ray); -} - bool Overlays::mousePressEvent(QMouseEvent* event) { PerformanceTimer perfTimer("Overlays::mousePressEvent"); PickRay ray = qApp->computePickRay(event->x(), event->y()); - RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray); + RayToOverlayIntersectionResult rayPickResult = findRayIntersectionVector(ray, true, QVector(), + QVector()); if (rayPickResult.intersects) { _currentClickingOnOverlayID = rayPickResult.overlayID; @@ -901,7 +876,8 @@ bool Overlays::mouseDoublePressEvent(QMouseEvent* event) { PerformanceTimer perfTimer("Overlays::mouseDoublePressEvent"); PickRay ray = qApp->computePickRay(event->x(), event->y()); - RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray); + RayToOverlayIntersectionResult rayPickResult = findRayIntersectionVector(ray, true, QVector(), + QVector()); if (rayPickResult.intersects) { _currentClickingOnOverlayID = rayPickResult.overlayID; @@ -964,7 +940,8 @@ bool Overlays::mouseReleaseEvent(QMouseEvent* event) { PerformanceTimer perfTimer("Overlays::mouseReleaseEvent"); PickRay ray = qApp->computePickRay(event->x(), event->y()); - RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray); + RayToOverlayIntersectionResult rayPickResult = findRayIntersectionVector(ray, true, QVector(), + QVector()); if (rayPickResult.intersects) { auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Release); mouseReleasePointerEvent(rayPickResult.overlayID, pointerEvent); @@ -993,7 +970,8 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) { PerformanceTimer perfTimer("Overlays::mouseMoveEvent"); PickRay ray = qApp->computePickRay(event->x(), event->y()); - RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray); + RayToOverlayIntersectionResult rayPickResult = findRayIntersectionVector(ray, true, QVector(), + QVector()); if (rayPickResult.intersects) { auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Move); mouseMovePointerEvent(rayPickResult.overlayID, pointerEvent); diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 21b9e93648..208fc8d78d 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -44,8 +44,7 @@ void OverlayPropertyResultFromScriptValue(const QScriptValue& object, OverlayPro const OverlayID UNKNOWN_OVERLAY_ID = OverlayID(); /**jsdoc - * The result of a {@link PickRay} search using {@link Overlays.findRayIntersection|findRayIntersection} or - * {@link Overlays.findRayIntersectionVector|findRayIntersectionVector}. + * The result of a {@link PickRay} search using {@link Overlays.findRayIntersection|findRayIntersection}. * @typedef {object} Overlays.RayToOverlayIntersectionResult * @property {boolean} intersects - true if the {@link PickRay} intersected with a 3D overlay, otherwise * false. @@ -383,7 +382,11 @@ public slots: OverlayPropertyResult getOverlaysProperties(const QVariant& overlaysProperties); /**jsdoc - * Find the closest 3D overlay intersected by a {@link PickRay}. + * Find the closest 3D overlay intersected by a {@link PickRay}. Overlays with their drawInFront property set + * to true have priority over overlays that don't, except that tablet overlays have priority over any + * drawInFront overlays behind them. I.e., if a drawInFront overlay is behind one that isn't + * drawInFront, the drawInFront overlay is returned, but if a tablet overlay is in front of a + * drawInFront overlay, the tablet overlay is returned. * @function Overlays.findRayIntersection * @param {PickRay} pickRay - The PickRay to use for finding overlays. * @param {boolean} [precisionPicking=false] - Unused; exists to match Entity API. @@ -750,8 +753,6 @@ private: OverlayID _currentClickingOnOverlayID { UNKNOWN_OVERLAY_ID }; OverlayID _currentHoverOverOverlayID { UNKNOWN_OVERLAY_ID }; - RayToOverlayIntersectionResult findRayIntersectionForMouseEvent(PickRay ray); - private slots: void mousePressPointerEvent(const OverlayID& overlayID, const PointerEvent& event); void mouseMovePointerEvent(const OverlayID& overlayID, const PointerEvent& event); diff --git a/interface/src/ui/overlays/Planar3DOverlay.cpp b/interface/src/ui/overlays/Planar3DOverlay.cpp index cf2691bb13..c33d3a0c39 100644 --- a/interface/src/ui/overlays/Planar3DOverlay.cpp +++ b/interface/src/ui/overlays/Planar3DOverlay.cpp @@ -64,7 +64,7 @@ void Planar3DOverlay::setProperties(const QVariantMap& properties) { */ QVariant Planar3DOverlay::getProperty(const QString& property) { if (property == "dimensions" || property == "scale" || property == "size") { - return vec2toVariant(getDimensions()); + return vec2ToVariant(getDimensions()); } return Base3DOverlay::getProperty(property); diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp index 48d89fab1c..73606c0467 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp +++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp @@ -51,9 +51,8 @@ void Rectangle3DOverlay::render(RenderArgs* args) { } float alpha = getAlpha(); - xColor color = getColor(); - const float MAX_COLOR = 255.0f; - glm::vec4 rectangleColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); + glm::u8vec3 color = getColor(); + glm::vec4 rectangleColor(toGlm(color), alpha); auto batch = args->_batch; if (batch) { diff --git a/interface/src/ui/overlays/Shape3DOverlay.cpp b/interface/src/ui/overlays/Shape3DOverlay.cpp index b0d3cf32af..b424424369 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.cpp +++ b/interface/src/ui/overlays/Shape3DOverlay.cpp @@ -30,9 +30,8 @@ void Shape3DOverlay::render(RenderArgs* args) { } float alpha = getAlpha(); - xColor color = getColor(); - const float MAX_COLOR = 255.0f; - glm::vec4 cubeColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); + glm::u8vec3 color = getColor(); + glm::vec4 shapeColor(toGlm(color), alpha); auto batch = args->_batch; if (batch) { @@ -44,9 +43,9 @@ void Shape3DOverlay::render(RenderArgs* args) { batch->setModelTransform(getRenderTransform()); if (_isSolid) { - geometryCache->renderSolidShapeInstance(args, *batch, _shape, cubeColor, shapePipeline); + geometryCache->renderSolidShapeInstance(args, *batch, _shape, shapeColor, shapePipeline); } else { - geometryCache->renderWireShapeInstance(args, *batch, _shape, cubeColor, shapePipeline); + geometryCache->renderWireShapeInstance(args, *batch, _shape, shapeColor, shapePipeline); } } } diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp index 00a0dd686c..97294ae871 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp @@ -77,9 +77,8 @@ void Sphere3DOverlay::render(RenderArgs* args) { } float alpha = getAlpha(); - xColor color = getColor(); - const float MAX_COLOR = 255.0f; - glm::vec4 sphereColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); + glm::u8vec3 color = getColor(); + glm::vec4 sphereColor(toGlm(color), alpha); auto batch = args->_batch; diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index 40e49ccefd..c8f8550e8e 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -64,21 +64,21 @@ void Text3DOverlay::setText(const QString& text) { _text = text; } -xColor Text3DOverlay::getBackgroundColor() { +glm::u8vec3 Text3DOverlay::getBackgroundColor() { if (_colorPulse == 0.0f) { return _backgroundColor; } float pulseLevel = updatePulse(); - xColor result = _backgroundColor; + glm::u8vec3 result = _backgroundColor; if (_colorPulse < 0.0f) { - result.red *= (1.0f - pulseLevel); - result.green *= (1.0f - pulseLevel); - result.blue *= (1.0f - pulseLevel); + result.x *= (1.0f - pulseLevel); + result.y *= (1.0f - pulseLevel); + result.z *= (1.0f - pulseLevel); } else { - result.red *= pulseLevel; - result.green *= pulseLevel; - result.blue *= pulseLevel; + result.x *= pulseLevel; + result.y *= pulseLevel; + result.z *= pulseLevel; } return result; } @@ -94,10 +94,8 @@ void Text3DOverlay::render(RenderArgs* args) { auto transform = getRenderTransform(); batch.setModelTransform(transform); - const float MAX_COLOR = 255.0f; - xColor backgroundColor = getBackgroundColor(); - glm::vec4 quadColor(backgroundColor.red / MAX_COLOR, backgroundColor.green / MAX_COLOR, - backgroundColor.blue / MAX_COLOR, getBackgroundAlpha()); + glm::u8vec3 backgroundColor = getBackgroundColor(); + glm::vec4 quadColor(toGlm(backgroundColor), getBackgroundAlpha()); glm::vec2 dimensions = getDimensions(); glm::vec2 halfDimensions = dimensions * 0.5f; @@ -122,8 +120,7 @@ void Text3DOverlay::render(RenderArgs* args) { transform.setScale(scaleFactor); batch.setModelTransform(transform); - glm::vec4 textColor = { _color.red / MAX_COLOR, _color.green / MAX_COLOR, - _color.blue / MAX_COLOR, getTextAlpha() }; + glm::vec4 textColor = { toGlm(_color), getTextAlpha() }; // FIXME: Factor out textRenderer so that Text3DOverlay overlay parts can be grouped by pipeline for a gpu performance increase. _textRenderer->draw(batch, 0, 0, getText(), textColor, glm::vec2(-1.0f), true); @@ -164,7 +161,7 @@ void Text3DOverlay::setProperties(const QVariantMap& properties) { bool valid; auto backgroundColor = properties["backgroundColor"]; if (backgroundColor.isValid()) { - auto color = xColorFromVariant(backgroundColor, valid); + auto color = u8vec3FromVariant(backgroundColor, valid); if (valid) { _backgroundColor = color; } @@ -260,7 +257,7 @@ QVariant Text3DOverlay::getProperty(const QString& property) { return _textAlpha; } if (property == "backgroundColor") { - return xColorToVariant(_backgroundColor); + return u8vec3ColortoVariant(_backgroundColor); } if (property == "backgroundAlpha") { return Billboard3DOverlay::getProperty("alpha"); diff --git a/interface/src/ui/overlays/Text3DOverlay.h b/interface/src/ui/overlays/Text3DOverlay.h index 21163101d0..16bbdcb4c4 100644 --- a/interface/src/ui/overlays/Text3DOverlay.h +++ b/interface/src/ui/overlays/Text3DOverlay.h @@ -39,7 +39,7 @@ public: float getTopMargin() const { return _topMargin; } float getRightMargin() const { return _rightMargin; } float getBottomMargin() const { return _bottomMargin; } - xColor getBackgroundColor(); + glm::u8vec3 getBackgroundColor(); float getTextAlpha() { return _textAlpha; } float getBackgroundAlpha() { return getAlpha(); } bool isTransparent() override { return Overlay::isTransparent() || _textAlpha < 1.0f; } @@ -65,7 +65,7 @@ private: QString _text; mutable QMutex _mutex; // used to make get/setText threadsafe, mutable so can be used in const functions - xColor _backgroundColor = xColor { 0, 0, 0 }; + glm::u8vec3 _backgroundColor { 0, 0, 0 }; float _textAlpha { 1.0f }; float _lineHeight { 1.0f }; float _leftMargin { 0.1f }; diff --git a/libraries/animation/src/AnimVariant.cpp b/libraries/animation/src/AnimVariant.cpp index 21fe234f7b..fe43fd8855 100644 --- a/libraries/animation/src/AnimVariant.cpp +++ b/libraries/animation/src/AnimVariant.cpp @@ -40,7 +40,7 @@ QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine, target.setProperty(name, value.getString()); break; case AnimVariant::Type::Vec3: - target.setProperty(name, vec3toScriptValue(engine, value.getVec3())); + target.setProperty(name, vec3ToScriptValue(engine, value.getVec3())); break; case AnimVariant::Type::Quat: target.setProperty(name, quatToScriptValue(engine, value.getQuat())); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 91d4e0f9d3..341b554949 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1253,7 +1253,6 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab const glm::mat4& rigToSensorMatrix, const glm::mat4& sensorToRigMatrix) { const bool ENABLE_POLE_VECTORS = true; - const float ELBOW_POLE_VECTOR_BLEND_FACTOR = 0.95f; if (leftHandEnabled) { @@ -1279,33 +1278,16 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab bool usePoleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, oppositeArmJointIndex, poleVector); if (usePoleVector) { glm::vec3 sensorPoleVector = transformVectorFast(rigToSensorMatrix, poleVector); - - if (_smoothPoleVectors) { - // smooth toward desired pole vector from previous pole vector... to reduce jitter - if (!_prevLeftHandPoleVectorValid) { - _prevLeftHandPoleVectorValid = true; - _prevLeftHandPoleVector = sensorPoleVector; - } - glm::quat deltaRot = rotationBetween(_prevLeftHandPoleVector, sensorPoleVector); - glm::quat smoothDeltaRot = safeMix(deltaRot, Quaternions::IDENTITY, ELBOW_POLE_VECTOR_BLEND_FACTOR); - _prevLeftHandPoleVector = smoothDeltaRot * _prevLeftHandPoleVector; - } else { - _prevLeftHandPoleVector = sensorPoleVector; - } _animVars.set("leftHandPoleVectorEnabled", true); _animVars.set("leftHandPoleReferenceVector", Vectors::UNIT_X); - _animVars.set("leftHandPoleVector", transformVectorFast(sensorToRigMatrix, _prevLeftHandPoleVector)); + _animVars.set("leftHandPoleVector", transformVectorFast(sensorToRigMatrix, sensorPoleVector)); } else { - _prevLeftHandPoleVectorValid = false; _animVars.set("leftHandPoleVectorEnabled", false); } - } else { - _prevLeftHandPoleVectorValid = false; _animVars.set("leftHandPoleVectorEnabled", false); } } else { - _prevLeftHandPoleVectorValid = false; _animVars.set("leftHandPoleVectorEnabled", false); _animVars.unset("leftHandPosition"); @@ -1344,33 +1326,16 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab bool usePoleVector = calculateElbowPoleVector(handJointIndex, elbowJointIndex, armJointIndex, oppositeArmJointIndex, poleVector); if (usePoleVector) { glm::vec3 sensorPoleVector = transformVectorFast(rigToSensorMatrix, poleVector); - - if (_smoothPoleVectors) { - // smooth toward desired pole vector from previous pole vector... to reduce jitter - if (!_prevRightHandPoleVectorValid) { - _prevRightHandPoleVectorValid = true; - _prevRightHandPoleVector = sensorPoleVector; - } - glm::quat deltaRot = rotationBetween(_prevRightHandPoleVector, sensorPoleVector); - glm::quat smoothDeltaRot = safeMix(deltaRot, Quaternions::IDENTITY, ELBOW_POLE_VECTOR_BLEND_FACTOR); - _prevRightHandPoleVector = smoothDeltaRot * _prevRightHandPoleVector; - } else { - _prevRightHandPoleVector = sensorPoleVector; - } - _animVars.set("rightHandPoleVectorEnabled", true); _animVars.set("rightHandPoleReferenceVector", -Vectors::UNIT_X); - _animVars.set("rightHandPoleVector", transformVectorFast(sensorToRigMatrix, _prevRightHandPoleVector)); + _animVars.set("rightHandPoleVector", transformVectorFast(sensorToRigMatrix, sensorPoleVector)); } else { - _prevRightHandPoleVectorValid = false; _animVars.set("rightHandPoleVectorEnabled", false); } } else { - _prevRightHandPoleVectorValid = false; _animVars.set("rightHandPoleVectorEnabled", false); } } else { - _prevRightHandPoleVectorValid = false; _animVars.set("rightHandPoleVectorEnabled", false); _animVars.unset("rightHandPosition"); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 48f00d4e5d..ed0b70d4b6 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -227,7 +227,6 @@ public: const AnimVariantMap& getAnimVars() const { return _lastAnimVars; } const AnimContext::DebugStateMachineMap& getStateMachineMap() const { return _lastContext.getStateMachineMap(); } - void toggleSmoothPoleVectors() { _smoothPoleVectors = !_smoothPoleVectors; }; signals: void onLoadComplete(); @@ -381,14 +380,6 @@ protected: glm::vec3 _prevLeftFootPoleVector { Vectors::UNIT_Z }; // sensor space bool _prevLeftFootPoleVectorValid { false }; - glm::vec3 _prevRightHandPoleVector{ -Vectors::UNIT_Z }; // sensor space - bool _prevRightHandPoleVectorValid{ false }; - - glm::vec3 _prevLeftHandPoleVector{ -Vectors::UNIT_Z }; // sensor space - bool _prevLeftHandPoleVectorValid{ false }; - - bool _smoothPoleVectors { false }; - int _rigId; bool _headEnabled { false }; diff --git a/libraries/audio/src/AudioInjectorOptions.cpp b/libraries/audio/src/AudioInjectorOptions.cpp index 0f4ab7ff42..295da1506e 100644 --- a/libraries/audio/src/AudioInjectorOptions.cpp +++ b/libraries/audio/src/AudioInjectorOptions.cpp @@ -34,7 +34,7 @@ AudioInjectorOptions::AudioInjectorOptions() : QScriptValue injectorOptionsToScriptValue(QScriptEngine* engine, const AudioInjectorOptions& injectorOptions) { QScriptValue obj = engine->newObject(); - obj.setProperty("position", vec3toScriptValue(engine, injectorOptions.position)); + obj.setProperty("position", vec3ToScriptValue(engine, injectorOptions.position)); obj.setProperty("volume", injectorOptions.volume); obj.setProperty("loop", injectorOptions.loop); obj.setProperty("orientation", quatToScriptValue(engine, injectorOptions.orientation)); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index f154746707..6770cd7f96 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -173,7 +173,6 @@ AvatarTransit::Status AvatarTransit::updatePosition(float deltaTime) { Status status = Status::IDLE; if (_isTransiting) { float nextTime = _currentTime + deltaTime; - glm::vec3 newPosition; if (nextTime >= _totalTime) { _currentPosition = _endPosition; _isTransiting = false; @@ -704,6 +703,19 @@ static TextRenderer3D* textRenderer(TextRendererType type) { return displayNameRenderer; } +void Avatar::metaBlendshapeOperator(int blendshapeNumber, const QVector& blendshapeOffsets, const QVector& blendedMeshSizes, + const render::ItemIDs& subItemIDs) { + render::Transaction transaction; + transaction.updateItem(_renderItemID, [blendshapeNumber, blendshapeOffsets, blendedMeshSizes, + subItemIDs](AvatarData& avatar) { + auto avatarPtr = dynamic_cast(&avatar); + if (avatarPtr) { + avatarPtr->setBlendedVertices(blendshapeNumber, blendshapeOffsets, blendedMeshSizes, subItemIDs); + } + }); + AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction); +} + void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { auto avatarPayload = new render::Payload(self); auto avatarPayloadPointer = std::shared_ptr>(avatarPayload); @@ -713,7 +725,8 @@ void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& sc // INitialize the _render bound as we are creating the avatar render item _renderBound = getBounds(); transaction.resetItem(_renderItemID, avatarPayloadPointer); - _skeletonModel->addToScene(scene, transaction); + using namespace std::placeholders; + _skeletonModel->addToScene(scene, transaction, std::bind(&Avatar::metaBlendshapeOperator, this, _1, _2, _3, _4)); _skeletonModel->setTagMask(render::hifi::TAG_ALL_VIEWS); _skeletonModel->setGroupCulled(true); _skeletonModel->setCanCastShadow(true); @@ -792,7 +805,7 @@ void Avatar::updateRenderItem(render::Transaction& transaction) { avatarPtr->_renderBound = renderBound; } } - ); + ); } } @@ -936,7 +949,8 @@ void Avatar::fixupModelsInScene(const render::ScenePointer& scene) { render::Transaction transaction; if (_skeletonModel->isRenderable() && _skeletonModel->needsFixupInScene()) { _skeletonModel->removeFromScene(scene, transaction); - _skeletonModel->addToScene(scene, transaction); + using namespace std::placeholders; + _skeletonModel->addToScene(scene, transaction, std::bind(&Avatar::metaBlendshapeOperator, this, _1, _2, _3, _4)); _skeletonModel->setTagMask(render::hifi::TAG_ALL_VIEWS); _skeletonModel->setGroupCulled(true); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 1087f74c07..3f4cf7b8c2 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -28,6 +28,8 @@ #include "Rig.h" #include +#include "MetaModelPayload.h" + namespace render { template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar); template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar); @@ -108,7 +110,7 @@ private: float _scale { 1.0f }; }; -class Avatar : public AvatarData, public scriptable::ModelProvider { +class Avatar : public AvatarData, public scriptable::ModelProvider, public MetaModelPayload { Q_OBJECT // This property has JSDoc in MyAvatar.h. @@ -620,6 +622,9 @@ protected: static const float ATTACHMENT_LOADING_PRIORITY; LoadingStatus _loadingStatus { LoadingStatus::NoModel }; + + void metaBlendshapeOperator(int blendshapeNumber, const QVector& blendshapeOffsets, const QVector& blendedMeshSizes, + const render::ItemIDs& subItemIDs); }; #endif // hifi_Avatar_h diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index d78d83bc09..6cd2101a0e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -75,8 +75,11 @@ size_t AvatarDataPacket::maxJointDataSize(size_t numJoints, bool hasGrabJoints) totalSize += numJoints * sizeof(SixByteTrans); // Translations size_t NUM_FAUX_JOINT = 2; - size_t num_grab_joints = (hasGrabJoints ? 2 : 0); - totalSize += (NUM_FAUX_JOINT + num_grab_joints) * (sizeof(SixByteQuat) + sizeof(SixByteTrans)); // faux joints + totalSize += NUM_FAUX_JOINT * (sizeof(SixByteQuat) + sizeof(SixByteTrans)); // faux joints + + if (hasGrabJoints) { + totalSize += sizeof(AvatarDataPacket::FarGrabJoints); + } return totalSize; } @@ -690,7 +693,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent if (hasGrabJoints) { // the far-grab joints may range further than 3 meters, so we can't use packFloatVec3ToSignedTwoByteFixed etc auto startSection = destinationBuffer; - auto data = reinterpret_cast(destinationBuffer); + glm::vec3 leftFarGrabPosition = extractTranslation(leftFarGrabMatrix); glm::quat leftFarGrabRotation = extractRotation(leftFarGrabMatrix); glm::vec3 rightFarGrabPosition = extractTranslation(rightFarGrabMatrix); @@ -698,28 +701,17 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent glm::vec3 mouseFarGrabPosition = extractTranslation(mouseFarGrabMatrix); glm::quat mouseFarGrabRotation = extractRotation(mouseFarGrabMatrix); - AVATAR_MEMCPY(leftFarGrabPosition); - // Can't do block copy as struct order is x, y, z, w. - data->leftFarGrabRotation[0] = leftFarGrabRotation.w; - data->leftFarGrabRotation[1] = leftFarGrabRotation.x; - data->leftFarGrabRotation[2] = leftFarGrabRotation.y; - data->leftFarGrabRotation[3] = leftFarGrabRotation.z; - destinationBuffer += sizeof(data->leftFarGrabPosition); - - AVATAR_MEMCPY(rightFarGrabPosition); - data->rightFarGrabRotation[0] = rightFarGrabRotation.w; - data->rightFarGrabRotation[1] = rightFarGrabRotation.x; - data->rightFarGrabRotation[2] = rightFarGrabRotation.y; - data->rightFarGrabRotation[3] = rightFarGrabRotation.z; - destinationBuffer += sizeof(data->rightFarGrabRotation); - - AVATAR_MEMCPY(mouseFarGrabPosition); - data->mouseFarGrabRotation[0] = mouseFarGrabRotation.w; - data->mouseFarGrabRotation[1] = mouseFarGrabRotation.x; - data->mouseFarGrabRotation[2] = mouseFarGrabRotation.y; - data->mouseFarGrabRotation[3] = mouseFarGrabRotation.z; - destinationBuffer += sizeof(data->mouseFarGrabRotation); + AvatarDataPacket::FarGrabJoints farGrabJoints = { + { leftFarGrabPosition.x, leftFarGrabPosition.y, leftFarGrabPosition.z }, + { leftFarGrabRotation.w, leftFarGrabRotation.x, leftFarGrabRotation.y, leftFarGrabRotation.z }, + { rightFarGrabPosition.x, rightFarGrabPosition.y, rightFarGrabPosition.z }, + { rightFarGrabRotation.w, rightFarGrabRotation.x, rightFarGrabRotation.y, rightFarGrabRotation.z }, + { mouseFarGrabPosition.x, mouseFarGrabPosition.y, mouseFarGrabPosition.z }, + { mouseFarGrabRotation.w, mouseFarGrabRotation.x, mouseFarGrabRotation.y, mouseFarGrabRotation.z } + }; + memcpy(destinationBuffer, &farGrabJoints, sizeof(farGrabJoints)); + destinationBuffer += sizeof(AvatarDataPacket::FarGrabJoints); int numBytes = destinationBuffer - startSection; if (outboundDataRateOut) { @@ -1250,25 +1242,37 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { auto startSection = sourceBuffer; PACKET_READ_CHECK(FarGrabJoints, sizeof(AvatarDataPacket::FarGrabJoints)); - auto data = reinterpret_cast(sourceBuffer); - glm::vec3 leftFarGrabPosition = glm::vec3(data->leftFarGrabPosition[0], data->leftFarGrabPosition[1], - data->leftFarGrabPosition[2]); - glm::quat leftFarGrabRotation = glm::quat(data->leftFarGrabRotation[0], data->leftFarGrabRotation[1], - data->leftFarGrabRotation[2], data->leftFarGrabRotation[3]); - glm::vec3 rightFarGrabPosition = glm::vec3(data->rightFarGrabPosition[0], data->rightFarGrabPosition[1], - data->rightFarGrabPosition[2]); - glm::quat rightFarGrabRotation = glm::quat(data->rightFarGrabRotation[0], data->rightFarGrabRotation[1], - data->rightFarGrabRotation[2], data->rightFarGrabRotation[3]); - glm::vec3 mouseFarGrabPosition = glm::vec3(data->mouseFarGrabPosition[0], data->mouseFarGrabPosition[1], - data->mouseFarGrabPosition[2]); - glm::quat mouseFarGrabRotation = glm::quat(data->mouseFarGrabRotation[0], data->mouseFarGrabRotation[1], - data->mouseFarGrabRotation[2], data->mouseFarGrabRotation[3]); + + AvatarDataPacket::FarGrabJoints farGrabJoints; + memcpy(&farGrabJoints, sourceBuffer, sizeof(farGrabJoints)); // to avoid misaligned floats + + glm::vec3 leftFarGrabPosition = glm::vec3(farGrabJoints.leftFarGrabPosition[0], + farGrabJoints.leftFarGrabPosition[1], + farGrabJoints.leftFarGrabPosition[2]); + glm::quat leftFarGrabRotation = glm::quat(farGrabJoints.leftFarGrabRotation[0], + farGrabJoints.leftFarGrabRotation[1], + farGrabJoints.leftFarGrabRotation[2], + farGrabJoints.leftFarGrabRotation[3]); + glm::vec3 rightFarGrabPosition = glm::vec3(farGrabJoints.rightFarGrabPosition[0], + farGrabJoints.rightFarGrabPosition[1], + farGrabJoints.rightFarGrabPosition[2]); + glm::quat rightFarGrabRotation = glm::quat(farGrabJoints.rightFarGrabRotation[0], + farGrabJoints.rightFarGrabRotation[1], + farGrabJoints.rightFarGrabRotation[2], + farGrabJoints.rightFarGrabRotation[3]); + glm::vec3 mouseFarGrabPosition = glm::vec3(farGrabJoints.mouseFarGrabPosition[0], + farGrabJoints.mouseFarGrabPosition[1], + farGrabJoints.mouseFarGrabPosition[2]); + glm::quat mouseFarGrabRotation = glm::quat(farGrabJoints.mouseFarGrabRotation[0], + farGrabJoints.mouseFarGrabRotation[1], + farGrabJoints.mouseFarGrabRotation[2], + farGrabJoints.mouseFarGrabRotation[3]); _farGrabLeftMatrixCache.set(createMatFromQuatAndPos(leftFarGrabRotation, leftFarGrabPosition)); _farGrabRightMatrixCache.set(createMatFromQuatAndPos(rightFarGrabRotation, rightFarGrabPosition)); _farGrabMouseMatrixCache.set(createMatFromQuatAndPos(mouseFarGrabRotation, mouseFarGrabPosition)); - sourceBuffer += sizeof(AvatarDataPacket::AvatarGlobalPosition); + sourceBuffer += sizeof(AvatarDataPacket::FarGrabJoints); int numBytesRead = sourceBuffer - startSection; _farGrabJointRate.increment(numBytesRead); _farGrabJointUpdateRate.increment(); @@ -2828,10 +2832,10 @@ QScriptValue RayToAvatarIntersectionResultToScriptValue(QScriptEngine* engine, c obj.setProperty("avatarID", avatarIDValue); obj.setProperty("distance", value.distance); obj.setProperty("face", boxFaceToString(value.face)); + QScriptValue intersection = vec3ToScriptValue(engine, value.intersection); - QScriptValue intersection = vec3toScriptValue(engine, value.intersection); obj.setProperty("intersection", intersection); - QScriptValue surfaceNormal = vec3toScriptValue(engine, value.surfaceNormal); + QScriptValue surfaceNormal = vec3ToScriptValue(engine, value.surfaceNormal); obj.setProperty("surfaceNormal", surfaceNormal); obj.setProperty("extraInfo", engine->toScriptValue(value.extraInfo)); return obj; diff --git a/libraries/controllers/src/controllers/Pose.cpp b/libraries/controllers/src/controllers/Pose.cpp index 967838ef84..6df4b4af81 100644 --- a/libraries/controllers/src/controllers/Pose.cpp +++ b/libraries/controllers/src/controllers/Pose.cpp @@ -41,10 +41,10 @@ namespace controller { */ QScriptValue Pose::toScriptValue(QScriptEngine* engine, const Pose& pose) { QScriptValue obj = engine->newObject(); - obj.setProperty("translation", vec3toScriptValue(engine, pose.translation)); + obj.setProperty("translation", vec3ToScriptValue(engine, pose.translation)); obj.setProperty("rotation", quatToScriptValue(engine, pose.rotation)); - obj.setProperty("velocity", vec3toScriptValue(engine, pose.velocity)); - obj.setProperty("angularVelocity", vec3toScriptValue(engine, pose.angularVelocity)); + obj.setProperty("velocity", vec3ToScriptValue(engine, pose.velocity)); + obj.setProperty("angularVelocity", vec3ToScriptValue(engine, pose.angularVelocity)); obj.setProperty("valid", pose.valid); return obj; } diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp index a0d5cb0920..d3cb602e5b 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp @@ -494,7 +494,7 @@ glm::mat4 CompositorHelper::getPoint2DTransform(const glm::vec2& point, float si QVariant ReticleInterface::getPosition() const { - return vec2toVariant(_compositor->getReticlePosition()); + return vec2ToVariant(_compositor->getReticlePosition()); } void ReticleInterface::setPosition(QVariant position) { diff --git a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp index 4ea293a3da..d746ec1ea9 100644 --- a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp @@ -41,7 +41,7 @@ void LightEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint float largestDiameter = glm::compMax(dimensions); light->setMaximumRadius(largestDiameter / 2.0f); - light->setColor(toGlm(entity->getXColor())); + light->setColor(toGlm(entity->getColor())); float intensity = entity->getIntensity();//* entity->getFadingRatio(); light->setIntensity(intensity); diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp index 9ac7e9921f..6a472cab1e 100644 --- a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp @@ -37,7 +37,7 @@ void LineEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointe if (_lineVerticesID == GeometryCache::UNKNOWN_ID) { _lineVerticesID = geometryCache->allocateID(); } - glm::vec4 lineColor(toGlm(entity->getXColor()), entity->getLocalRenderAlpha()); + glm::vec4 lineColor(toGlm(entity->getColor()), entity->getLocalRenderAlpha()); geometryCache->updateVertices(_lineVerticesID, _linePoints, lineColor); } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index b5f9d9f833..2d31549872 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -24,8 +24,6 @@ #include "RenderableEntityItem.h" - - class Model; class EntityTreeRenderer; diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 743df477ac..0d9e948db8 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -142,7 +142,7 @@ void PolyLineEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo } if (strokeColorsChanged) { _lastStrokeColors = entity->getStrokeColors(); - _lastStrokeColors = _lastNormals.size() == _lastStrokeColors.size() ? _lastStrokeColors : QVector({ toGlm(entity->getXColor()) }); + _lastStrokeColors = _lastNormals.size() == _lastStrokeColors.size() ? _lastStrokeColors : QVector({ toGlm(entity->getColor()) }); } if (pointsChanged || strokeWidthsChanged || normalsChanged || strokeColorsChanged) { _empty = std::min(_lastPoints.size(), std::min(_lastNormals.size(), _lastStrokeWidths.size())) < 2; @@ -161,10 +161,10 @@ void PolyLineEntityRenderer::updateGeometry(const std::vector& vertices) _verticesBuffer->setSubData(0, vertices); } -std::vector PolyLineEntityRenderer::updateVertices(const QVector& points, - const QVector& normals, +std::vector PolyLineEntityRenderer::updateVertices(const QVector& points, + const QVector& normals, const QVector& strokeWidths, - const QVector& strokeColors, + const QVector& strokeColors, const bool isUVModeStretch, const float textureAspectRatio) { // Calculate the minimum vector size out of normals, points, and stroke widths diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index 3ba26c74df..8130171da8 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -52,10 +52,10 @@ protected: }; void updateGeometry(const std::vector& vertices); - static std::vector updateVertices(const QVector& points, - const QVector& normals, + static std::vector updateVertices(const QVector& points, + const QVector& normals, const QVector& strokeWidths, - const QVector& strokeColors, + const QVector& strokeColors, const bool isUVModeStretch, const float textureAspectRatio); diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index 08a3b585e4..ce9e7ab764 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -47,11 +47,11 @@ bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint return true; } - if (_textColor != toGlm(entity->getTextColorX())) { + if (_textColor != toGlm(entity->getTextColor())) { return true; } - if (_backgroundColor != toGlm(entity->getBackgroundColorX())) { + if (_backgroundColor != toGlm(entity->getBackgroundColor())) { return true; } @@ -77,8 +77,8 @@ void TextEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen } void TextEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { - _textColor = toGlm(entity->getTextColorX()); - _backgroundColor = toGlm(entity->getBackgroundColorX()); + _textColor = toGlm(entity->getTextColor()); + _backgroundColor = toGlm(entity->getBackgroundColor()); _faceCamera = entity->getFaceCamera(); _lineHeight = entity->getLineHeight(); _text = entity->getText(); diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index cf452c9cf7..2c017da71d 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -413,10 +413,10 @@ void ZoneEntityRenderer::updateHazeFromEntity(const TypedEntityPointer& entity) haze->setAltitudeBased(_hazeProperties.getHazeAltitudeEffect()); haze->setHazeRangeFactor(graphics::Haze::convertHazeRangeToHazeRangeFactor(_hazeProperties.getHazeRange())); - xColor hazeColor = _hazeProperties.getHazeColor(); - haze->setHazeColor(glm::vec3(hazeColor.red / 255.0, hazeColor.green / 255.0, hazeColor.blue / 255.0)); - xColor hazeGlareColor = _hazeProperties.getHazeGlareColor(); - haze->setHazeGlareColor(glm::vec3(hazeGlareColor.red / 255.0, hazeGlareColor.green / 255.0, hazeGlareColor.blue / 255.0)); + glm::u8vec3 hazeColor = _hazeProperties.getHazeColor(); + haze->setHazeColor(toGlm(hazeColor)); + glm::u8vec3 hazeGlareColor = _hazeProperties.getHazeGlareColor(); + haze->setHazeGlareColor(toGlm(hazeGlareColor)); haze->setHazeEnableGlare(_hazeProperties.getHazeEnableGlare()); haze->setHazeGlareBlend(graphics::Haze::convertGlareAngleToPower(_hazeProperties.getHazeGlareAngle())); @@ -447,7 +447,7 @@ void ZoneEntityRenderer::updateKeyBackgroundFromEntity(const TypedEntityPointer& setSkyboxMode((ComponentMode)entity->getSkyboxMode()); editBackground(); - setSkyboxColor(_skyboxProperties.getColorVec3()); + setSkyboxColor(toGlm(_skyboxProperties.getColor())); setProceduralUserData(entity->getUserData()); setSkyboxURL(_skyboxProperties.getURL()); } diff --git a/libraries/entities/src/EntityEditFilters.cpp b/libraries/entities/src/EntityEditFilters.cpp index 94df7eb465..6f7e012bc4 100644 --- a/libraries/entities/src/EntityEditFilters.cpp +++ b/libraries/entities/src/EntityEditFilters.cpp @@ -104,10 +104,10 @@ bool EntityEditFilters::filter(glm::vec3& position, EntityItemProperties& proper AABox aaBox = zoneEntity->getAABox(success); if (success) { QScriptValue boundingBox = filterData.engine->newObject(); - QScriptValue bottomRightNear = vec3toScriptValue(filterData.engine, aaBox.getCorner()); - QScriptValue topFarLeft = vec3toScriptValue(filterData.engine, aaBox.calcTopFarLeft()); - QScriptValue center = vec3toScriptValue(filterData.engine, aaBox.calcCenter()); - QScriptValue boundingBoxDimensions = vec3toScriptValue(filterData.engine, aaBox.getDimensions()); + QScriptValue bottomRightNear = vec3ToScriptValue(filterData.engine, aaBox.getCorner()); + QScriptValue topFarLeft = vec3ToScriptValue(filterData.engine, aaBox.calcTopFarLeft()); + QScriptValue center = vec3ToScriptValue(filterData.engine, aaBox.calcCenter()); + QScriptValue boundingBoxDimensions = vec3ToScriptValue(filterData.engine, aaBox.getDimensions()); boundingBox.setProperty("brn", bottomRightNear); boundingBox.setProperty("tfl", topFarLeft); boundingBox.setProperty("center", center); diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 184bb054ea..36ffc68fd3 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -131,6 +131,10 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_CLONE_AVATAR_ENTITY; requestedProperties += PROP_CLONE_ORIGIN_ID; + withReadLock([&] { + requestedProperties += _grabProperties.getEntityProperties(params); + }); + return requestedProperties; } @@ -168,6 +172,9 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet EntityPropertyFlags propertyFlags(PROP_LAST_ITEM); EntityPropertyFlags requestedProperties = getEntityProperties(params); + requestedProperties -= PROP_CLIENT_ONLY; + requestedProperties -= PROP_OWNING_AVATAR_ID; + // If we are being called for a subsequent pass at appendEntityData() that failed to completely encode this item, // then our entityTreeElementExtraEncodeData should include data about which properties we need to append. if (entityTreeElementExtraEncodeData && entityTreeElementExtraEncodeData->entities.contains(getEntityItemID())) { @@ -300,7 +307,12 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_CLONE_LIMIT, getCloneLimit()); APPEND_ENTITY_PROPERTY(PROP_CLONE_DYNAMIC, getCloneDynamic()); APPEND_ENTITY_PROPERTY(PROP_CLONE_AVATAR_ENTITY, getCloneAvatarEntity()); - APPEND_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, getCloneOriginID()); + APPEND_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, getCloneOriginID()); + + withReadLock([&] { + _grabProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); + }); appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, @@ -661,7 +673,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef const QUuid& myNodeID = nodeList->getSessionUUID(); bool weOwnSimulation = _simulationOwner.matchesValidID(myNodeID); - // pack SimulationOwner and terse update properties near each other + // pack SimulationOwner, transform, and velocity properties near each other // NOTE: the server is authoritative for changes to simOwnerID so we always unpack ownership data // even when we would otherwise ignore the rest of the packet. @@ -892,7 +904,15 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_CLONE_LIMIT, float, setCloneLimit); READ_ENTITY_PROPERTY(PROP_CLONE_DYNAMIC, bool, setCloneDynamic); READ_ENTITY_PROPERTY(PROP_CLONE_AVATAR_ENTITY, bool, setCloneAvatarEntity); - READ_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, QUuid, setCloneOriginID); + READ_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, QUuid, setCloneOriginID); + + withWriteLock([&] { + int bytesFromGrab = _grabProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, + somethingChanged); + bytesRead += bytesFromGrab; + dataAt += bytesFromGrab; + }); bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData, somethingChanged); @@ -1329,13 +1349,16 @@ EntityItemProperties EntityItem::getProperties(const EntityPropertyFlags& desire COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneAvatarEntity, getCloneAvatarEntity); COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneOriginID, getCloneOriginID); + withReadLock([&] { + _grabProperties.getProperties(properties); + }); + properties._defaultSettings = false; return properties; } -void EntityItem::getAllTerseUpdateProperties(EntityItemProperties& properties) const { - // a TerseUpdate includes the transform and its derivatives +void EntityItem::getTransformAndVelocityProperties(EntityItemProperties& properties) const { if (!properties._positionChanged) { properties._position = getLocalPosition(); } @@ -1359,8 +1382,11 @@ void EntityItem::getAllTerseUpdateProperties(EntityItemProperties& properties) c properties._accelerationChanged = true; } -void EntityItem::setScriptSimulationPriority(uint8_t priority) { - uint8_t newPriority = stillHasGrabActions() ? glm::max(priority, SCRIPT_GRAB_SIMULATION_PRIORITY) : priority; +void EntityItem::upgradeScriptSimulationPriority(uint8_t priority) { + uint8_t newPriority = glm::max(priority, _scriptSimulationPriority); + if (newPriority < SCRIPT_GRAB_SIMULATION_PRIORITY && stillHasGrabActions()) { + newPriority = SCRIPT_GRAB_SIMULATION_PRIORITY; + } if (newPriority != _scriptSimulationPriority) { // set the dirty flag to trigger a bid or ownership update markDirtyFlags(Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY); @@ -1395,7 +1421,7 @@ bool EntityItem::stillWaitingToTakeOwnership(uint64_t timestamp) const { bool EntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChanged = false; - // these affect TerseUpdate properties + // these affect transform and velocity properties SET_ENTITY_PROPERTY_FROM_PROPERTIES(simulationOwner, setSimulationOwner); SET_ENTITY_PROPERTY_FROM_PROPERTIES(position, setPosition); SET_ENTITY_PROPERTY_FROM_PROPERTIES(rotation, setRotation); @@ -1465,6 +1491,11 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneAvatarEntity, setCloneAvatarEntity); SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneOriginID, setCloneOriginID); + withWriteLock([&] { + bool grabPropertiesChanged = _grabProperties.setProperties(properties); + somethingChanged |= grabPropertiesChanged; + }); + if (updateQueryAACube()) { somethingChanged = true; } @@ -1784,6 +1815,12 @@ void EntityItem::setUnscaledDimensions(const glm::vec3& value) { } } +glm::vec3 EntityItem::getUnscaledDimensions() const { + return resultWithReadLock([&] { + return _unscaledDimensions; + }); +} + void EntityItem::setRotation(glm::quat rotation) { if (getLocalOrientation() != rotation) { setLocalOrientation(rotation); @@ -3221,3 +3258,26 @@ void EntityItem::setScriptHasFinishedPreload(bool value) { bool EntityItem::isScriptPreloadFinished() { return _scriptPreloadFinished; } + +void EntityItem::prepareForSimulationOwnershipBid(EntityItemProperties& properties, uint64_t now, uint8_t priority) { + if (dynamicDataNeedsTransmit()) { + setDynamicDataNeedsTransmit(false); + properties.setActionData(getDynamicData()); + } + + if (updateQueryAACube()) { + // due to parenting, the server may not know where something is in world-space, so include the bounding cube. + properties.setQueryAACube(getQueryAACube()); + } + + // set the LastEdited of the properties but NOT the entity itself + properties.setLastEdited(now); + + clearScriptSimulationPriority(); + properties.setSimulationOwner(Physics::getSessionUUID(), priority); + setPendingOwnershipPriority(priority); + + properties.setClientOnly(getClientOnly()); + properties.setOwningAvatarID(getOwningAvatarID()); + setLastBroadcast(now); // for debug/physics status icons +} diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 0e5cae66b6..c49017b2e0 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -35,6 +35,7 @@ #include "SimulationOwner.h" #include "SimulationFlags.h" #include "EntityDynamicInterface.h" +#include "GrabPropertyGroup.h" #include "graphics/Material.h" @@ -191,7 +192,7 @@ public: virtual void setScaledDimensions(const glm::vec3& value); virtual glm::vec3 getRaycastDimensions() const { return getScaledDimensions(); } - inline const glm::vec3 getUnscaledDimensions() const { return _unscaledDimensions; } + glm::vec3 getUnscaledDimensions() const; virtual void setUnscaledDimensions(const glm::vec3& value); float getLocalRenderAlpha() const; @@ -263,9 +264,8 @@ public: void setCollisionSoundURL(const QString& value); glm::vec3 getRegistrationPoint() const; /// registration point as ratio of entity - /// registration point as ratio of entity - virtual void setRegistrationPoint(const glm::vec3& value); // FIXME: this is suspicious! + virtual void setRegistrationPoint(const glm::vec3& value); // FIXME: this is suspicious! bool hasAngularVelocity() const { return getWorldAngularVelocity() != ENTITY_ITEM_ZERO_VEC3; } bool hasLocalAngularVelocity() const { return getLocalAngularVelocity() != ENTITY_ITEM_ZERO_VEC3; } @@ -325,7 +325,7 @@ public: // TODO: move this "ScriptSimulationPriority" and "PendingOwnership" stuff into EntityMotionState // but first would need to do some other cleanup. In the meantime these live here as "scratch space" // to allow libs that don't know about each other to communicate. - void setScriptSimulationPriority(uint8_t priority); + void upgradeScriptSimulationPriority(uint8_t priority); void clearScriptSimulationPriority(); uint8_t getScriptSimulationPriority() const { return _scriptSimulationPriority; } void setPendingOwnershipPriority(uint8_t priority); @@ -420,7 +420,7 @@ public: quint64 getLastEditedFromRemote() const { return _lastEditedFromRemote; } void updateLastEditedFromRemote() { _lastEditedFromRemote = usecTimestampNow(); } - void getAllTerseUpdateProperties(EntityItemProperties& properties) const; + void getTransformAndVelocityProperties(EntityItemProperties& properties) const; void flagForMotionStateChange() { _flags |= Simulation::DIRTY_MOTION_TYPE; } @@ -533,6 +533,10 @@ public: void setCloneIDs(const QVector& cloneIDs); void setVisuallyReady(bool visuallyReady) { _visuallyReady = visuallyReady; } + const GrabPropertyGroup& getGrabProperties() const { return _grabProperties; } + + void prepareForSimulationOwnershipBid(EntityItemProperties& properties, uint64_t now, uint8_t priority); + signals: void requestRenderUpdate(); void spaceUpdate(std::pair data); @@ -711,6 +715,8 @@ protected: QUuid _cloneOriginID; QVector _cloneIDs; + GrabPropertyGroup _grabProperties; + private: std::unordered_map _materials; std::mutex _materialsLock; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 7e404afa3f..c90d1f6635 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -40,6 +40,7 @@ HazePropertyGroup EntityItemProperties::_staticHaze; BloomPropertyGroup EntityItemProperties::_staticBloom; KeyLightPropertyGroup EntityItemProperties::_staticKeyLight; AmbientLightPropertyGroup EntityItemProperties::_staticAmbientLight; +GrabPropertyGroup EntityItemProperties::_staticGrab; EntityPropertyList PROP_LAST_ITEM = (EntityPropertyList)(PROP_AFTER_LAST_ITEM - 1); @@ -61,8 +62,8 @@ EntityItemProperties::EntityItemProperties(EntityPropertyFlags desiredProperties } -void EntityItemProperties::calculateNaturalPosition(const glm::vec3& min, const glm::vec3& max) { - glm::vec3 halfDimension = (max - min) / 2.0f; +void EntityItemProperties::calculateNaturalPosition(const vec3& min, const vec3& max) { + vec3 halfDimension = (max - min) / 2.0f; _naturalPosition = max - halfDimension; } @@ -86,6 +87,7 @@ void EntityItemProperties::debugDump() const { getKeyLight().debugDump(); getAmbientLight().debugDump(); getBloom().debugDump(); + getGrab().debugDump(); qCDebug(entities) << " changed properties..."; EntityPropertyFlags props = getChangedProperties(); @@ -471,6 +473,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { changedProperties += _skybox.getChangedProperties(); changedProperties += _haze.getChangedProperties(); changedProperties += _bloom.getChangedProperties(); + changedProperties += _grab.getChangedProperties(); return changedProperties; } @@ -628,6 +631,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * avatar entities: their clientOnly property will be set to true. * @property {Uuid} cloneOriginID - The ID of the entity that this entity was cloned from. * + * @property {Entities.Grab} grab - The grab-related properties. + * * @property {string} itemName="" - Certifiable name of the Marketplace item. * @property {string} itemDescription="" - Certifiable description of the Marketplace item. * @property {string} itemCategories="" - Certifiable category of the Marketplace item. @@ -872,9 +877,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @property {number} emitSpeed=5 - The speed, in m/s, that each particle is emitted at. * @property {number} speedSpread=1 - The spread in speeds at which particles are emitted at. If emitSpeed == 5 * and speedSpread == 1, particles will be emitted with speeds in the range 4m/s – 6m/s. - * @property {vec3} emitAcceleration=0,-9.8,0 - The acceleration that is applied to each particle during its lifetime. The + * @property {Vec3} emitAcceleration=0,-9.8,0 - The acceleration that is applied to each particle during its lifetime. The * default is Earth's gravity value. - * @property {vec3} accelerationSpread=0,0,0 - The spread in accelerations that each particle is given. If + * @property {Vec3} accelerationSpread=0,0,0 - The spread in accelerations that each particle is given. If * emitAccelerations == {x: 0, y: -9.8, z: 0} and accelerationSpread == * {x: 0, y: 1, z: 0}, each particle will have an acceleration in the range {x: 0, y: -10.8, z: 0} * – {x: 0, y: -8.8, z: 0}. @@ -887,7 +892,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * default, particles emit along the entity's local z-axis, and azimuthStart and azimuthFinish * are relative to the entity's local x-axis. The default value is a rotation of -90 degrees about the local x-axis, i.e., * the particles emit vertically. - * @property {vec3} emitDimensions=0,0,0 - The dimensions of the ellipsoid from which particles are emitted. + * @property {Vec3} emitDimensions=0,0,0 - The dimensions of the ellipsoid from which particles are emitted. * @property {number} emitRadiusStart=1 - The starting radius within the ellipsoid at which particles start being emitted; * range 0.01.0 for the ellipsoid center to the ellipsoid surface, respectively. * Particles are emitted from the portion of the ellipsoid that lies between emitRadiusStart and the @@ -916,9 +921,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * and radiusSpread == 0.25, each particle will have a radius in the range 0.25 – * 0.75. * @property {Color} color=255,255,255 - The color of each particle at the middle of its life. - * @property {Color} colorStart={} - The color of each particle at the start of its life. If any of the component values are + * @property {ColorFloat} colorStart={} - The color of each particle at the start of its life. If any of the component values are * undefined, the color value is used. - * @property {Color} colorFinish={} - The color of each particle at the end of its life. If any of the component values are + * @property {ColorFloat} colorFinish={} - The color of each particle at the end of its life. If any of the component values are * undefined, the color value is used. * @property {Color} colorSpread=0,0,0 - The spread in color that each particle is given. If * color == {red: 100, green: 100, blue: 100} and colorSpread == @@ -1314,9 +1319,6 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NAME, name); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLLISION_SOUND_URL, collisionSoundURL); - // Light, Line, Model, ParticleEffect, PolyLine, Shape - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLOR, color); - // Particles only if (_type == EntityTypes::ParticleEffect) { COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMITTING_PARTICLES, isEmitting); @@ -1338,9 +1340,10 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RADIUS_SPREAD, radiusSpread); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RADIUS_START, radiusStart); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RADIUS_FINISH, radiusFinish); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLOR_SPREAD, colorSpread); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLOR_START, colorStart); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLOR_FINISH, colorFinish); + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR_SPREAD, colorSpread, u8vec3Color); + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR_START, colorStart, vec3Color); + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR_FINISH, colorFinish, vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_SPREAD, alphaSpread); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_START, alphaStart); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA_FINISH, alphaFinish); @@ -1363,6 +1366,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_TRANSLATIONS_SET, jointTranslationsSet); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_TRANSLATIONS, jointTranslations); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RELAY_PARENT_JOINTS, relayParentJoints); + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); } if (_type == EntityTypes::Model || _type == EntityTypes::Zone || _type == EntityTypes::ParticleEffect) { @@ -1379,6 +1383,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool if (_type == EntityTypes::Box || _type == EntityTypes::Sphere || _type == EntityTypes::Shape) { COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SHAPE, shape); + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); } // FIXME - it seems like ParticleEffect should also support this @@ -1393,6 +1398,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool // Lights only if (_type == EntityTypes::Light) { + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IS_SPOTLIGHT, isSpotlight); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_INTENSITY, intensity); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FALLOFF_RADIUS, falloffRadius); @@ -1404,8 +1410,8 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool if (_type == EntityTypes::Text) { COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXT, text); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_HEIGHT, lineHeight); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_TEXT_COLOR, textColor, getTextColor()); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BACKGROUND_COLOR, backgroundColor, getBackgroundColor()); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_TYPED(PROP_TEXT_COLOR, textColor, getTextColor(), u8vec3Color); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_TYPED(PROP_BACKGROUND_COLOR, backgroundColor, getBackgroundColor(), u8vec3Color); } // Zones only @@ -1459,10 +1465,11 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool // Lines & PolyLines if (_type == EntityTypes::Line || _type == EntityTypes::PolyLine) { + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_WIDTH, lineWidth); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_POINTS, linePoints); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NORMALS, normals); // Polyline only. - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_STROKE_COLORS, strokeColors); // Polyline only. + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_STROKE_COLORS, strokeColors, qVectorVec3Color); // Polyline only. COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_STROKE_WIDTHS, strokeWidths); // Polyline only. COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXTURES, textures); // Polyline only. COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IS_UV_MODE_STRETCH, isUVModeStretch); // Polyline only. @@ -1493,10 +1500,10 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool AABox aaBox = getAABox(); QScriptValue boundingBox = engine->newObject(); - QScriptValue bottomRightNear = vec3toScriptValue(engine, aaBox.getCorner()); - QScriptValue topFarLeft = vec3toScriptValue(engine, aaBox.calcTopFarLeft()); - QScriptValue center = vec3toScriptValue(engine, aaBox.calcCenter()); - QScriptValue boundingBoxDimensions = vec3toScriptValue(engine, aaBox.getDimensions()); + QScriptValue bottomRightNear = vec3ToScriptValue(engine, aaBox.getCorner()); + QScriptValue topFarLeft = vec3ToScriptValue(engine, aaBox.calcTopFarLeft()); + QScriptValue center = vec3ToScriptValue(engine, aaBox.calcCenter()); + QScriptValue boundingBoxDimensions = vec3ToScriptValue(engine, aaBox.getDimensions()); boundingBox.setProperty("brn", bottomRightNear); boundingBox.setProperty("tfl", topFarLeft); boundingBox.setProperty("center", center); @@ -1530,6 +1537,8 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_AVATAR_ENTITY, cloneAvatarEntity); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_ORIGIN_ID, cloneOriginID); + _grab.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + // Rendering info if (!skipDefaults && !strictSemantics && (!psuedoPropertyFlagsActive || psueudoPropertyFlags.test(EntityPsuedoPropertyFlag::RenderInfo))) { @@ -1598,10 +1607,10 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(angularDamping, float, setAngularDamping); COPY_PROPERTY_FROM_QSCRIPTVALUE(visible, bool, setVisible); COPY_PROPERTY_FROM_QSCRIPTVALUE(canCastShadow, bool, setCanCastShadow); - COPY_PROPERTY_FROM_QSCRIPTVALUE(color, xColor, setColor); - COPY_PROPERTY_FROM_QSCRIPTVALUE(colorSpread, xColor, setColorSpread); - COPY_PROPERTY_FROM_QSCRIPTVALUE(colorStart, vec3, setColorStart); - COPY_PROPERTY_FROM_QSCRIPTVALUE(colorFinish, vec3, setColorFinish); + COPY_PROPERTY_FROM_QSCRIPTVALUE(color, u8vec3Color, setColor); + COPY_PROPERTY_FROM_QSCRIPTVALUE(colorSpread, u8vec3Color, setColorSpread); + COPY_PROPERTY_FROM_QSCRIPTVALUE(colorStart, vec3Color, setColorStart); + COPY_PROPERTY_FROM_QSCRIPTVALUE(colorFinish, vec3Color, setColorFinish); COPY_PROPERTY_FROM_QSCRIPTVALUE(alpha, float, setAlpha); COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaSpread, float, setAlphaSpread); COPY_PROPERTY_FROM_QSCRIPTVALUE(alphaStart, float, setAlphaStart); @@ -1626,8 +1635,8 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(userData, QString, setUserData); COPY_PROPERTY_FROM_QSCRIPTVALUE(text, QString, setText); COPY_PROPERTY_FROM_QSCRIPTVALUE(lineHeight, float, setLineHeight); - COPY_PROPERTY_FROM_QSCRIPTVALUE(textColor, xColor, setTextColor); - COPY_PROPERTY_FROM_QSCRIPTVALUE(backgroundColor, xColor, setBackgroundColor); + COPY_PROPERTY_FROM_QSCRIPTVALUE(textColor, u8vec3Color, setTextColor); + COPY_PROPERTY_FROM_QSCRIPTVALUE(backgroundColor, u8vec3Color, setBackgroundColor); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(shapeType, ShapeType); COPY_PROPERTY_FROM_QSCRIPTVALUE(maxParticles, quint32, setMaxParticles); COPY_PROPERTY_FROM_QSCRIPTVALUE(lifespan, float, setLifespan); @@ -1700,7 +1709,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(strokeColors, qVectorVec3, setStrokeColors); COPY_PROPERTY_FROM_QSCRIPTVALUE(strokeWidths,qVectorFloat, setStrokeWidths); COPY_PROPERTY_FROM_QSCRIPTVALUE(isUVModeStretch, bool, setIsUVModeStretch); - + if (!honorReadOnly) { // this is used by the json reader to set things that we don't want javascript to able to affect. @@ -1718,6 +1727,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool _skybox.copyFromScriptValue(object, _defaultSettings); _haze.copyFromScriptValue(object, _defaultSettings); _bloom.copyFromScriptValue(object, _defaultSettings); + _grab.copyFromScriptValue(object, _defaultSettings); COPY_PROPERTY_FROM_QSCRIPTVALUE(xTextureURL, QString, setXTextureURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(yTextureURL, QString, setYTextureURL); @@ -1883,6 +1893,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { _skybox.merge(other._skybox); _haze.merge(other._haze); _bloom.merge(other._bloom); + _grab.merge(other._grab); COPY_PROPERTY_IF_CHANGED(xTextureURL); COPY_PROPERTY_IF_CHANGED(yTextureURL); @@ -1973,13 +1984,13 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue std::call_once(initMap, [](){ ADD_PROPERTY_TO_MAP(PROP_VISIBLE, Visible, visible, bool); ADD_PROPERTY_TO_MAP(PROP_CAN_CAST_SHADOW, CanCastShadow, canCastShadow, bool); - ADD_PROPERTY_TO_MAP(PROP_POSITION, Position, position, glm::vec3); - ADD_PROPERTY_TO_MAP(PROP_DIMENSIONS, Dimensions, dimensions, glm::vec3); - ADD_PROPERTY_TO_MAP(PROP_ROTATION, Rotation, rotation, glm::quat); + ADD_PROPERTY_TO_MAP(PROP_POSITION, Position, position, vec3); + ADD_PROPERTY_TO_MAP(PROP_DIMENSIONS, Dimensions, dimensions, vec3); + ADD_PROPERTY_TO_MAP(PROP_ROTATION, Rotation, rotation, quat); ADD_PROPERTY_TO_MAP(PROP_DENSITY, Density, density, float); - ADD_PROPERTY_TO_MAP(PROP_VELOCITY, Velocity, velocity, glm::vec3); - ADD_PROPERTY_TO_MAP(PROP_GRAVITY, Gravity, gravity, glm::vec3); - ADD_PROPERTY_TO_MAP(PROP_ACCELERATION, Acceleration, acceleration, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_VELOCITY, Velocity, velocity, vec3); + ADD_PROPERTY_TO_MAP(PROP_GRAVITY, Gravity, gravity, vec3); + ADD_PROPERTY_TO_MAP(PROP_ACCELERATION, Acceleration, acceleration, vec3); ADD_PROPERTY_TO_MAP(PROP_DAMPING, Damping, damping, float); ADD_PROPERTY_TO_MAP(PROP_RESTITUTION, Restitution, restitution, float); ADD_PROPERTY_TO_MAP(PROP_FRICTION, Friction, friction, float); @@ -1988,10 +1999,10 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_SCRIPT_TIMESTAMP, ScriptTimestamp, scriptTimestamp, quint64); ADD_PROPERTY_TO_MAP(PROP_SERVER_SCRIPTS, ServerScripts, serverScripts, QString); ADD_PROPERTY_TO_MAP(PROP_COLLISION_SOUND_URL, CollisionSoundURL, collisionSoundURL, QString); - ADD_PROPERTY_TO_MAP(PROP_COLOR, Color, color, xColor); - ADD_PROPERTY_TO_MAP(PROP_COLOR_SPREAD, ColorSpread, colorSpread, xColor); - ADD_PROPERTY_TO_MAP(PROP_COLOR_START, ColorStart, colorStart, vec3); - ADD_PROPERTY_TO_MAP(PROP_COLOR_FINISH, ColorFinish, colorFinish, vec3); + ADD_PROPERTY_TO_MAP(PROP_COLOR, Color, color, u8vec3Color); + ADD_PROPERTY_TO_MAP(PROP_COLOR_SPREAD, ColorSpread, colorSpread, u8vec3Color); + ADD_PROPERTY_TO_MAP(PROP_COLOR_START, ColorStart, colorStart, vec3Color); + ADD_PROPERTY_TO_MAP(PROP_COLOR_FINISH, ColorFinish, colorFinish, vec3Color); ADD_PROPERTY_TO_MAP(PROP_ALPHA, Alpha, alpha, float); ADD_PROPERTY_TO_MAP(PROP_ALPHA_SPREAD, AlphaSpread, alphaSpread, float); ADD_PROPERTY_TO_MAP(PROP_ALPHA_START, AlphaStart, alphaStart, float); @@ -1999,8 +2010,8 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_EMITTER_SHOULD_TRAIL, EmitterShouldTrail, emitterShouldTrail, bool); ADD_PROPERTY_TO_MAP(PROP_MODEL_URL, ModelURL, modelURL, QString); ADD_PROPERTY_TO_MAP(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString); - ADD_PROPERTY_TO_MAP(PROP_REGISTRATION_POINT, RegistrationPoint, registrationPoint, glm::vec3); - ADD_PROPERTY_TO_MAP(PROP_ANGULAR_VELOCITY, AngularVelocity, angularVelocity, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_REGISTRATION_POINT, RegistrationPoint, registrationPoint, vec3); + ADD_PROPERTY_TO_MAP(PROP_ANGULAR_VELOCITY, AngularVelocity, angularVelocity, vec3); ADD_PROPERTY_TO_MAP(PROP_ANGULAR_DAMPING, AngularDamping, angularDamping, float); ADD_PROPERTY_TO_MAP(PROP_COLLISIONLESS, Collisionless, collisionless, bool); ADD_PROPERTY_TO_MAP(PROP_COLLISIONLESS, unused, ignoreForCollisions, unused); // legacy support @@ -2019,24 +2030,24 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_SIMULATION_OWNER, SimulationOwner, simulationOwner, SimulationOwner); ADD_PROPERTY_TO_MAP(PROP_TEXT, Text, text, QString); ADD_PROPERTY_TO_MAP(PROP_LINE_HEIGHT, LineHeight, lineHeight, float); - ADD_PROPERTY_TO_MAP(PROP_TEXT_COLOR, TextColor, textColor, xColor); - ADD_PROPERTY_TO_MAP(PROP_BACKGROUND_COLOR, BackgroundColor, backgroundColor, xColor); + ADD_PROPERTY_TO_MAP(PROP_TEXT_COLOR, TextColor, textColor, u8vec3Color); + ADD_PROPERTY_TO_MAP(PROP_BACKGROUND_COLOR, BackgroundColor, backgroundColor, u8vec3Color); ADD_PROPERTY_TO_MAP(PROP_SHAPE_TYPE, ShapeType, shapeType, ShapeType); ADD_PROPERTY_TO_MAP(PROP_MAX_PARTICLES, MaxParticles, maxParticles, quint32); ADD_PROPERTY_TO_MAP(PROP_LIFESPAN, Lifespan, lifespan, float); ADD_PROPERTY_TO_MAP(PROP_EMITTING_PARTICLES, IsEmitting, isEmitting, bool); ADD_PROPERTY_TO_MAP(PROP_EMIT_RATE, EmitRate, emitRate, float); - ADD_PROPERTY_TO_MAP(PROP_EMIT_SPEED, EmitSpeed, emitSpeed, glm::vec3); - ADD_PROPERTY_TO_MAP(PROP_SPEED_SPREAD, SpeedSpread, speedSpread, glm::vec3); - ADD_PROPERTY_TO_MAP(PROP_EMIT_ORIENTATION, EmitOrientation, emitOrientation, glm::quat); - ADD_PROPERTY_TO_MAP(PROP_EMIT_DIMENSIONS, EmitDimensions, emitDimensions, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_EMIT_SPEED, EmitSpeed, emitSpeed, vec3); + ADD_PROPERTY_TO_MAP(PROP_SPEED_SPREAD, SpeedSpread, speedSpread, vec3); + ADD_PROPERTY_TO_MAP(PROP_EMIT_ORIENTATION, EmitOrientation, emitOrientation, quat); + ADD_PROPERTY_TO_MAP(PROP_EMIT_DIMENSIONS, EmitDimensions, emitDimensions, vec3); ADD_PROPERTY_TO_MAP(PROP_EMIT_RADIUS_START, EmitRadiusStart, emitRadiusStart, float); ADD_PROPERTY_TO_MAP(PROP_POLAR_START, EmitPolarStart, polarStart, float); ADD_PROPERTY_TO_MAP(PROP_POLAR_FINISH, EmitPolarFinish, polarFinish, float); ADD_PROPERTY_TO_MAP(PROP_AZIMUTH_START, EmitAzimuthStart, azimuthStart, float); ADD_PROPERTY_TO_MAP(PROP_AZIMUTH_FINISH, EmitAzimuthFinish, azimuthFinish, float); - ADD_PROPERTY_TO_MAP(PROP_EMIT_ACCELERATION, EmitAcceleration, emitAcceleration, glm::vec3); - ADD_PROPERTY_TO_MAP(PROP_ACCELERATION_SPREAD, AccelerationSpread, accelerationSpread, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_EMIT_ACCELERATION, EmitAcceleration, emitAcceleration, vec3); + ADD_PROPERTY_TO_MAP(PROP_ACCELERATION_SPREAD, AccelerationSpread, accelerationSpread, vec3); ADD_PROPERTY_TO_MAP(PROP_PARTICLE_RADIUS, ParticleRadius, particleRadius, float); ADD_PROPERTY_TO_MAP(PROP_RADIUS_SPREAD, RadiusSpread, radiusSpread, float); ADD_PROPERTY_TO_MAP(PROP_RADIUS_START, RadiusStart, radiusStart, float); @@ -2072,24 +2083,24 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_CERTIFICATE_ID, CertificateID, certificateID, QString); ADD_PROPERTY_TO_MAP(PROP_STATIC_CERTIFICATE_VERSION, StaticCertificateVersion, staticCertificateVersion, quint32); - ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_COLOR, KeyLightColor, keyLightColor, xColor); + ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_COLOR, KeyLightColor, keyLightColor, u8vec3Color); ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_INTENSITY, KeyLightIntensity, keyLightIntensity, float); - ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_DIRECTION, KeyLightDirection, keyLightDirection, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_DIRECTION, KeyLightDirection, keyLightDirection, vec3); ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_CAST_SHADOW, KeyLightCastShadows, keyLightCastShadows, bool); - ADD_PROPERTY_TO_MAP(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, vec3); ADD_PROPERTY_TO_MAP(PROP_VOXEL_DATA, VoxelData, voxelData, QByteArray); ADD_PROPERTY_TO_MAP(PROP_VOXEL_SURFACE_STYLE, VoxelSurfaceStyle, voxelSurfaceStyle, uint16_t); ADD_PROPERTY_TO_MAP(PROP_NAME, Name, name, QString); ADD_PROPERTY_TO_MAP(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString); ADD_PROPERTY_TO_MAP(PROP_LINE_WIDTH, LineWidth, lineWidth, float); - ADD_PROPERTY_TO_MAP(PROP_LINE_POINTS, LinePoints, linePoints, QVector); + ADD_PROPERTY_TO_MAP(PROP_LINE_POINTS, LinePoints, linePoints, QVector); ADD_PROPERTY_TO_MAP(PROP_HREF, Href, href, QString); ADD_PROPERTY_TO_MAP(PROP_DESCRIPTION, Description, description, QString); ADD_PROPERTY_TO_MAP(PROP_FACE_CAMERA, FaceCamera, faceCamera, bool); ADD_PROPERTY_TO_MAP(PROP_ACTION_DATA, ActionData, actionData, QByteArray); - ADD_PROPERTY_TO_MAP(PROP_NORMALS, Normals, normals, QVector); - ADD_PROPERTY_TO_MAP(PROP_STROKE_COLORS, StrokeColors, strokeColors, QVector); + ADD_PROPERTY_TO_MAP(PROP_NORMALS, Normals, normals, QVector); + ADD_PROPERTY_TO_MAP(PROP_STROKE_COLORS, StrokeColors, strokeColors, QVector); ADD_PROPERTY_TO_MAP(PROP_STROKE_WIDTHS, StrokeWidths, strokeWidths, QVector); ADD_PROPERTY_TO_MAP(PROP_IS_UV_MODE_STRETCH, IsUVModeStretch, isUVModeStretch, QVector); ADD_PROPERTY_TO_MAP(PROP_X_TEXTURE_URL, XTextureURL, xTextureURL, QString); @@ -2105,16 +2116,16 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_PARENT_ID, ParentID, parentID, QUuid); ADD_PROPERTY_TO_MAP(PROP_PARENT_JOINT_INDEX, ParentJointIndex, parentJointIndex, uint16_t); - ADD_PROPERTY_TO_MAP(PROP_LOCAL_POSITION, LocalPosition, localPosition, glm::vec3); - ADD_PROPERTY_TO_MAP(PROP_LOCAL_ROTATION, LocalRotation, localRotation, glm::quat); - ADD_PROPERTY_TO_MAP(PROP_LOCAL_VELOCITY, LocalVelocity, localVelocity, glm::vec3); - ADD_PROPERTY_TO_MAP(PROP_LOCAL_ANGULAR_VELOCITY, LocalAngularVelocity, localAngularVelocity, glm::vec3); - ADD_PROPERTY_TO_MAP(PROP_LOCAL_DIMENSIONS, LocalDimensions, localDimensions, glm::vec3); + ADD_PROPERTY_TO_MAP(PROP_LOCAL_POSITION, LocalPosition, localPosition, vec3); + ADD_PROPERTY_TO_MAP(PROP_LOCAL_ROTATION, LocalRotation, localRotation, quat); + ADD_PROPERTY_TO_MAP(PROP_LOCAL_VELOCITY, LocalVelocity, localVelocity, vec3); + ADD_PROPERTY_TO_MAP(PROP_LOCAL_ANGULAR_VELOCITY, LocalAngularVelocity, localAngularVelocity, vec3); + ADD_PROPERTY_TO_MAP(PROP_LOCAL_DIMENSIONS, LocalDimensions, localDimensions, vec3); ADD_PROPERTY_TO_MAP(PROP_JOINT_ROTATIONS_SET, JointRotationsSet, jointRotationsSet, QVector); - ADD_PROPERTY_TO_MAP(PROP_JOINT_ROTATIONS, JointRotations, jointRotations, QVector); + ADD_PROPERTY_TO_MAP(PROP_JOINT_ROTATIONS, JointRotations, jointRotations, QVector); ADD_PROPERTY_TO_MAP(PROP_JOINT_TRANSLATIONS_SET, JointTranslationsSet, jointTranslationsSet, QVector); - ADD_PROPERTY_TO_MAP(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector); + ADD_PROPERTY_TO_MAP(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector); ADD_PROPERTY_TO_MAP(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool); ADD_PROPERTY_TO_MAP(PROP_SHAPE, Shape, shape, QString); @@ -2172,6 +2183,26 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_CLONE_AVATAR_ENTITY, CloneAvatarEntity, cloneAvatarEntity, bool); ADD_PROPERTY_TO_MAP(PROP_CLONE_ORIGIN_ID, CloneOriginID, cloneOriginID, QUuid); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_GRABBABLE, Grab, grab, Grabbable, grabbable); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_KINEMATIC, Grab, grab, GrabKinematic, grabKinematic); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_FOLLOWS_CONTROLLER, Grab, grab, GrabFollowsController, grabFollowsController); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_TRIGGERABLE, Grab, grab, Triggerable, triggerable); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_EQUIPPABLE, Grab, grab, Equippable, equippable); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, Grab, grab, + EquippableLeftPosition, equippableLeftPosition); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, Grab, grab, + EquippableLeftRotation, equippableLeftRotation); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, Grab, grab, + EquippableRightPosition, equippableRightPosition); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, Grab, grab, + EquippableRightRotation, equippableRightRotation); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, Grab, grab, + EquippableIndicatorURL, equippableIndicatorURL); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, Grab, grab, + EquippableIndicatorScale, equippableIndicatorScale); + ADD_GROUP_PROPERTY_TO_MAP(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, Grab, grab, + EquippableIndicatorOffset, equippableIndicatorOffset); + // FIXME - these are not yet handled //ADD_PROPERTY_TO_MAP(PROP_CREATED, Created, created, quint64); @@ -2224,7 +2255,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy // Always include the root octcode. This is only because the OctreeEditPacketSender will check these octcodes // to determine which server to send the changes to in the case of multiple jurisdictions. The root will be sent // to all servers. - glm::vec3 rootPosition(0); + vec3 rootPosition(0); float rootScale = 0.5f; unsigned char* octcode = pointToOctalCode(rootPosition.x, rootPosition.y, rootPosition.z, rootScale); @@ -2309,7 +2340,6 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_SCRIPT, properties.getScript()); APPEND_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, properties.getScriptTimestamp()); APPEND_ENTITY_PROPERTY(PROP_SERVER_SCRIPTS, properties.getServerScripts()); - APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, properties.getRegistrationPoint()); APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, properties.getAngularVelocity()); APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, properties.getAngularDamping()); @@ -2344,6 +2374,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, properties.getCompoundShapeURL()); APPEND_ENTITY_PROPERTY(PROP_TEXTURES, properties.getTextures()); APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)(properties.getShapeType())); + APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); _staticAnimation.setProperties(properties); _staticAnimation.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); @@ -2356,8 +2387,8 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy } if (properties.getType() == EntityTypes::Light) { - APPEND_ENTITY_PROPERTY(PROP_IS_SPOTLIGHT, properties.getIsSpotlight()); APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); + APPEND_ENTITY_PROPERTY(PROP_IS_SPOTLIGHT, properties.getIsSpotlight()); APPEND_ENTITY_PROPERTY(PROP_INTENSITY, properties.getIntensity()); APPEND_ENTITY_PROPERTY(PROP_FALLOFF_RADIUS, properties.getFalloffRadius()); APPEND_ENTITY_PROPERTY(PROP_EXPONENT, properties.getExponent()); @@ -2385,6 +2416,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_RADIUS_SPREAD, properties.getRadiusSpread()); APPEND_ENTITY_PROPERTY(PROP_RADIUS_START, properties.getRadiusStart()); APPEND_ENTITY_PROPERTY(PROP_RADIUS_FINISH, properties.getRadiusFinish()); + APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); APPEND_ENTITY_PROPERTY(PROP_COLOR_SPREAD, properties.getColorSpread()); APPEND_ENTITY_PROPERTY(PROP_COLOR_START, properties.getColorStart()); APPEND_ENTITY_PROPERTY(PROP_COLOR_FINISH, properties.getColorFinish()); @@ -2445,11 +2477,13 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy } if (properties.getType() == EntityTypes::Line) { + APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); APPEND_ENTITY_PROPERTY(PROP_LINE_WIDTH, properties.getLineWidth()); APPEND_ENTITY_PROPERTY(PROP_LINE_POINTS, properties.getLinePoints()); } if (properties.getType() == EntityTypes::PolyLine) { + APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); APPEND_ENTITY_PROPERTY(PROP_LINE_WIDTH, properties.getLineWidth()); APPEND_ENTITY_PROPERTY(PROP_LINE_POINTS, properties.getLinePoints()); APPEND_ENTITY_PROPERTY(PROP_NORMALS, properties.getPackedNormals()); @@ -2464,6 +2498,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy properties.getType() == EntityTypes::Box || properties.getType() == EntityTypes::Sphere) { APPEND_ENTITY_PROPERTY(PROP_SHAPE, properties.getShape()); + APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); } // Materials @@ -2501,6 +2536,10 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_CLONE_LIMIT, properties.getCloneLimit()); APPEND_ENTITY_PROPERTY(PROP_CLONE_DYNAMIC, properties.getCloneDynamic()); APPEND_ENTITY_PROPERTY(PROP_CLONE_AVATAR_ENTITY, properties.getCloneAvatarEntity()); + + _staticGrab.setProperties(properties); + _staticGrab.appendToEditPacket(packetData, requestedProperties, propertyFlags, + propertiesDidntFit, propertyCount, appendState); } if (propertyCount > 0) { @@ -2562,7 +2601,7 @@ QByteArray EntityItemProperties::getPackedNormals() const { return packNormals(getNormals()); } -QByteArray EntityItemProperties::packNormals(const QVector& normals) const { +QByteArray EntityItemProperties::packNormals(const QVector& normals) const { int normalsSize = normals.size(); QByteArray packedNormals = QByteArray(normalsSize * 6 + 1, '0'); // add size of the array @@ -2579,7 +2618,7 @@ QByteArray EntityItemProperties::packNormals(const QVector& normals) QByteArray EntityItemProperties::getPackedStrokeColors() const { return packStrokeColors(getStrokeColors()); } -QByteArray EntityItemProperties::packStrokeColors(const QVector& strokeColors) const { +QByteArray EntityItemProperties::packStrokeColors(const QVector& strokeColors) const { int strokeColorsSize = strokeColors.size(); QByteArray packedStrokeColors = QByteArray(strokeColorsSize * 3 + 1, '0'); @@ -2589,9 +2628,9 @@ QByteArray EntityItemProperties::packStrokeColors(const QVector& stro for (int i = 0; i < strokeColorsSize; i++) { // add the color to the QByteArray - packedStrokeColors[i * 3 + 1] = strokeColors[i].r * 255; - packedStrokeColors[i * 3 + 2] = strokeColors[i].g * 255; - packedStrokeColors[i * 3 + 3] = strokeColors[i].b * 255; + packedStrokeColors[i * 3 + 1] = strokeColors[i].x * 255; + packedStrokeColors[i * 3 + 2] = strokeColors[i].y * 255; + packedStrokeColors[i * 3 + 3] = strokeColors[i].z * 255; } return packedStrokeColors; } @@ -2680,13 +2719,13 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int processedBytes += propertyFlags.getEncodedLength(); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SIMULATION_OWNER, QByteArray, setSimulationOwner); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POSITION, glm::vec3, setPosition); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DIMENSIONS, glm::vec3, setDimensions); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ROTATION, glm::quat, setRotation); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POSITION, vec3, setPosition); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DIMENSIONS, vec3, setDimensions); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ROTATION, quat, setRotation); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DENSITY, float, setDensity); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VELOCITY, glm::vec3, setVelocity); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GRAVITY, glm::vec3, setGravity); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACCELERATION, glm::vec3, setAcceleration); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VELOCITY, vec3, setVelocity); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GRAVITY, vec3, setGravity); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACCELERATION, vec3, setAcceleration); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DAMPING, float, setDamping); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RESTITUTION, float, setRestitution); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FRICTION, float, setFriction); @@ -2694,9 +2733,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCRIPT, QString, setScript); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCRIPT_TIMESTAMP, quint64, setScriptTimestamp); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SERVER_SCRIPTS, QString, setServerScripts); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, xColor, setColor); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_VELOCITY, glm::vec3, setAngularVelocity); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_REGISTRATION_POINT, vec3, setRegistrationPoint); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_VELOCITY, vec3, setAngularVelocity); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_DAMPING, float, setAngularDamping); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VISIBLE, bool, setVisible); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CAN_CAST_SHADOW, bool, setCanCastShadow); @@ -2719,8 +2757,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int if (properties.getType() == EntityTypes::Text) { READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT, QString, setText); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_HEIGHT, float, setLineHeight); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_COLOR, xColor, setTextColor); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BACKGROUND_COLOR, xColor, setBackgroundColor); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_COLOR, u8vec3Color, setTextColor); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BACKGROUND_COLOR, u8vec3Color, setBackgroundColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FACE_CAMERA, bool, setFaceCamera); } @@ -2729,19 +2767,20 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXTURES, QString, setTextures); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE_TYPE, ShapeType, setShapeType); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); properties.getAnimation().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_ROTATIONS_SET, QVector, setJointRotationsSet); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_ROTATIONS, QVector, setJointRotations); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_ROTATIONS, QVector, setJointRotations); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_TRANSLATIONS_SET, QVector, setJointTranslationsSet); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_TRANSLATIONS, QVector, setJointTranslations); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_TRANSLATIONS, QVector, setJointTranslations); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RELAY_PARENT_JOINTS, bool, setRelayParentJoints); } if (properties.getType() == EntityTypes::Light) { READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IS_SPOTLIGHT, bool, setIsSpotlight); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, xColor, setColor); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_INTENSITY, float, setIntensity); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FALLOFF_RADIUS, float, setFalloffRadius); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EXPONENT, float, setExponent); @@ -2756,22 +2795,23 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_RATE, float, setEmitRate); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_SPEED, float, setEmitSpeed); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SPEED_SPREAD, float, setSpeedSpread); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_ORIENTATION, glm::quat, setEmitOrientation); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_DIMENSIONS, glm::vec3, setEmitDimensions); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_ORIENTATION, quat, setEmitOrientation); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_DIMENSIONS, vec3, setEmitDimensions); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_RADIUS_START, float, setEmitRadiusStart); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POLAR_START, float, setPolarStart); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_POLAR_FINISH, float, setPolarFinish); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AZIMUTH_START, float, setAzimuthStart); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AZIMUTH_FINISH, float, setAzimuthFinish); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_ACCELERATION, glm::vec3, setEmitAcceleration); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACCELERATION_SPREAD, glm::vec3, setAccelerationSpread); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMIT_ACCELERATION, vec3, setEmitAcceleration); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ACCELERATION_SPREAD, vec3, setAccelerationSpread); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARTICLE_RADIUS, float, setParticleRadius); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RADIUS_SPREAD, float, setRadiusSpread); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RADIUS_START, float, setRadiusStart); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RADIUS_FINISH, float, setRadiusFinish); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR_SPREAD, xColor, setColorSpread); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR_START, vec3, setColorStart); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR_FINISH, vec3, setColorFinish); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR_SPREAD, u8vec3Color, setColorSpread); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR_START, vec3Color, setColorStart); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR_FINISH, vec3Color, setColorFinish); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_SPREAD, float, setAlphaSpread); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_START, float, setAlphaStart); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA_FINISH, float, setAlphaFinish); @@ -2794,7 +2834,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FLYING_ALLOWED, bool, setFlyingAllowed); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FILTER_URL, QString, setFilterURL); - + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HAZE_MODE, uint32_t, setHazeMode); properties.getHaze().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); @@ -2807,7 +2847,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int } if (properties.getType() == EntityTypes::PolyVox) { - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VOXEL_VOLUME_SIZE, glm::vec3, setVoxelVolumeSize); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VOXEL_VOLUME_SIZE, vec3, setVoxelVolumeSize); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VOXEL_DATA, QByteArray, setVoxelData); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VOXEL_SURFACE_STYLE, uint16_t, setVoxelSurfaceStyle); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_X_TEXTURE_URL, QString, setXTextureURL); @@ -2822,14 +2862,16 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int } if (properties.getType() == EntityTypes::Line) { + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_WIDTH, float, setLineWidth); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_POINTS, QVector, setLinePoints); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_POINTS, QVector, setLinePoints); } if (properties.getType() == EntityTypes::PolyLine) { + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_WIDTH, float, setLineWidth); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_POINTS, QVector, setLinePoints); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_POINTS, QVector, setLinePoints); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_NORMALS, QByteArray, setPackedNormals); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STROKE_COLORS, QByteArray, setPackedStrokeColors); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STROKE_WIDTHS, QVector, setStrokeWidths); @@ -2843,6 +2885,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int properties.getType() == EntityTypes::Box || properties.getType() == EntityTypes::Sphere) { READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE, QString, setShape); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); } // Materials @@ -2881,6 +2924,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_DYNAMIC, bool, setCloneDynamic); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_AVATAR_ENTITY, bool, setCloneAvatarEntity); + properties.getGrab().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + return valid; } @@ -2888,14 +2933,14 @@ void EntityItemProperties::setPackedNormals(const QByteArray& value) { setNormals(unpackNormals(value)); } -QVector EntityItemProperties::unpackNormals(const QByteArray& normals) { +QVector EntityItemProperties::unpackNormals(const QByteArray& normals) { // the size of the vector is packed first - QVector unpackedNormals = QVector((int)normals[0]); + QVector unpackedNormals = QVector((int)normals[0]); if ((int)normals[0] == normals.size() / 6) { int j = 0; for (int i = 1; i < normals.size();) { - glm::vec3 aux = glm::vec3(); + vec3 aux = vec3(); i += unpackFloatVec3FromSignedTwoByteFixed((unsigned char*)normals.data() + i, aux, 15); unpackedNormals[j] = aux; j++; @@ -2911,9 +2956,9 @@ void EntityItemProperties::setPackedStrokeColors(const QByteArray& value) { setStrokeColors(unpackStrokeColors(value)); } -QVector EntityItemProperties::unpackStrokeColors(const QByteArray& strokeColors) { +QVector EntityItemProperties::unpackStrokeColors(const QByteArray& strokeColors) { // the size of the vector is packed first - QVector unpackedStrokeColors = QVector((int)strokeColors[0]); + QVector unpackedStrokeColors = QVector((int)strokeColors[0]); if ((int)strokeColors[0] == strokeColors.size() / 3) { int j = 0; @@ -3120,6 +3165,7 @@ void EntityItemProperties::markAllChanged() { _skybox.markAllChanged(); _haze.markAllChanged(); _bloom.markAllChanged(); + _grab.markAllChanged(); _sourceUrlChanged = true; _voxelVolumeSizeChanged = true; @@ -3185,10 +3231,10 @@ void EntityItemProperties::markAllChanged() { AABox EntityItemProperties::getAABox() const { // _position represents the position of the registration point. - glm::vec3 registrationRemainder = glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint; + vec3 registrationRemainder = vec3(1.0f) - _registrationPoint; - glm::vec3 unrotatedMinRelativeToEntity = - (_dimensions * _registrationPoint); - glm::vec3 unrotatedMaxRelativeToEntity = _dimensions * registrationRemainder; + vec3 unrotatedMinRelativeToEntity = - (_dimensions * _registrationPoint); + vec3 unrotatedMaxRelativeToEntity = _dimensions * registrationRemainder; Extents unrotatedExtentsRelativeToRegistrationPoint = { unrotatedMinRelativeToEntity, unrotatedMaxRelativeToEntity }; Extents rotatedExtentsRelativeToRegistrationPoint = unrotatedExtentsRelativeToRegistrationPoint.getRotated(_rotation); @@ -3198,9 +3244,12 @@ AABox EntityItemProperties::getAABox() const { return AABox(rotatedExtentsRelativeToRegistrationPoint); } -bool EntityItemProperties::hasTerseUpdateChanges() const { - // a TerseUpdate includes the transform and its derivatives - return _positionChanged || _velocityChanged || _rotationChanged || _angularVelocityChanged || _accelerationChanged; +bool EntityItemProperties::hasTransformOrVelocityChanges() const { + return _positionChanged ||_localPositionChanged + || _rotationChanged || _localRotationChanged + || _velocityChanged || _localVelocityChanged + || _angularVelocityChanged || _localAngularVelocityChanged + || _accelerationChanged; } bool EntityItemProperties::hasMiscPhysicsChanges() const { @@ -3209,6 +3258,56 @@ bool EntityItemProperties::hasMiscPhysicsChanges() const { _compoundShapeURLChanged || _dynamicChanged || _collisionlessChanged || _collisionMaskChanged; } +bool EntityItemProperties::hasSimulationRestrictedChanges() const { + return _positionChanged || _localPositionChanged + || _rotationChanged || _localRotationChanged + || _velocityChanged || _localVelocityChanged + || _angularVelocityChanged || _localAngularVelocityChanged + || _accelerationChanged + || _parentIDChanged || _parentJointIndexChanged; +} + +void EntityItemProperties::copySimulationRestrictedProperties(const EntityItemPointer& entity) { + if (!_parentIDChanged) { + setParentID(entity->getParentID()); + } + if (!_parentJointIndexChanged) { + setParentJointIndex(entity->getParentJointIndex()); + } + if (!_localPositionChanged && !_positionChanged) { + setPosition(entity->getWorldPosition()); + } + if (!_localRotationChanged && !_rotationChanged) { + setRotation(entity->getWorldOrientation()); + } + if (!_localVelocityChanged && !_velocityChanged) { + setVelocity(entity->getWorldVelocity()); + } + if (!_localAngularVelocityChanged && !_angularVelocityChanged) { + setAngularVelocity(entity->getWorldAngularVelocity()); + } + if (!_accelerationChanged) { + setAcceleration(entity->getAcceleration()); + } + if (!_localDimensionsChanged && !_dimensionsChanged) { + setDimensions(entity->getScaledDimensions()); + } +} + +void EntityItemProperties::clearSimulationRestrictedProperties() { + _positionChanged = false; + _localPositionChanged = false; + _rotationChanged = false; + _localRotationChanged = false; + _velocityChanged = false; + _localVelocityChanged = false; + _angularVelocityChanged = false; + _localAngularVelocityChanged = false; + _accelerationChanged = false; + _parentIDChanged = false; + _parentJointIndexChanged = false; +} + void EntityItemProperties::clearSimulationOwner() { _simulationOwner.clear(); _simulationOwnerChanged = true; @@ -3227,6 +3326,20 @@ void EntityItemProperties::setSimulationOwner(const QByteArray& data) { } } +uint8_t EntityItemProperties::computeSimulationBidPriority() const { + uint8_t priority = 0; + if (_parentIDChanged || _parentJointIndexChanged) { + // we need higher simulation ownership priority to chang parenting info + priority = SCRIPT_GRAB_SIMULATION_PRIORITY; + } else if ( _positionChanged || _localPositionChanged + || _rotationChanged || _localRotationChanged + || _velocityChanged || _localVelocityChanged + || _angularVelocityChanged || _localAngularVelocityChanged) { + priority = SCRIPT_POKE_SIMULATION_PRIORITY; + } + return priority; +} + QList EntityItemProperties::listChangedProperties() { QList out; if (containsPositionChange()) { @@ -3654,6 +3767,7 @@ QList EntityItemProperties::listChangedProperties() { getSkybox().listChangedProperties(out); getHaze().listChangedProperties(out); getBloom().listChangedProperties(out); + getGrab().listChangedProperties(out); return out; } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 338ec5e071..ae2c402d22 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -58,6 +58,9 @@ const std::array COMPONENT_MODES = { { ComponentPair { COMPONENT_MODE_ENABLED, { "enabled" } } } }; +using vec3Color = glm::vec3; +using u8vec3Color = glm::u8vec3; + /// A collection of properties of an entity item used in the scripting API. Translates between the actual properties of an /// entity and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete /// set of entity item properties via JavaScript hashes/QScriptValues @@ -137,10 +140,10 @@ public: DEFINE_PROPERTY_REF(PROP_SCRIPT, Script, script, QString, ENTITY_ITEM_DEFAULT_SCRIPT); DEFINE_PROPERTY(PROP_SCRIPT_TIMESTAMP, ScriptTimestamp, scriptTimestamp, quint64, ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP); DEFINE_PROPERTY_REF(PROP_COLLISION_SOUND_URL, CollisionSoundURL, collisionSoundURL, QString, ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL); - DEFINE_PROPERTY_REF(PROP_COLOR, Color, color, xColor, ParticleEffectEntityItem::DEFAULT_XCOLOR); - DEFINE_PROPERTY_REF(PROP_COLOR_SPREAD, ColorSpread, colorSpread, xColor, ParticleEffectEntityItem::DEFAULT_XCOLOR_SPREAD); - DEFINE_PROPERTY_REF(PROP_COLOR_START, ColorStart, colorStart, vec3, particle::DEFAULT_COLOR_UNINITIALIZED); - DEFINE_PROPERTY_REF(PROP_COLOR_FINISH, ColorFinish, colorFinish, vec3, particle::DEFAULT_COLOR_UNINITIALIZED); + DEFINE_PROPERTY_REF(PROP_COLOR, Color, color, u8vec3Color, particle::DEFAULT_COLOR); + DEFINE_PROPERTY_REF(PROP_COLOR_SPREAD, ColorSpread, colorSpread, u8vec3Color, particle::DEFAULT_COLOR_SPREAD); + DEFINE_PROPERTY_REF(PROP_COLOR_START, ColorStart, colorStart, glm::vec3, particle::DEFAULT_COLOR_UNINITIALIZED); + DEFINE_PROPERTY_REF(PROP_COLOR_FINISH, ColorFinish, colorFinish, glm::vec3, particle::DEFAULT_COLOR_UNINITIALIZED); DEFINE_PROPERTY(PROP_ALPHA, Alpha, alpha, float, particle::DEFAULT_ALPHA); DEFINE_PROPERTY(PROP_ALPHA_SPREAD, AlphaSpread, alphaSpread, float, particle::DEFAULT_ALPHA_SPREAD); DEFINE_PROPERTY(PROP_ALPHA_START, AlphaStart, alphaStart, float, particle::DEFAULT_ALPHA_START); @@ -164,8 +167,8 @@ public: DEFINE_PROPERTY_REF(PROP_SIMULATION_OWNER, SimulationOwner, simulationOwner, SimulationOwner, SimulationOwner()); DEFINE_PROPERTY_REF(PROP_TEXT, Text, text, QString, TextEntityItem::DEFAULT_TEXT); DEFINE_PROPERTY(PROP_LINE_HEIGHT, LineHeight, lineHeight, float, TextEntityItem::DEFAULT_LINE_HEIGHT); - DEFINE_PROPERTY_REF(PROP_TEXT_COLOR, TextColor, textColor, xColor, TextEntityItem::DEFAULT_TEXT_COLOR); - DEFINE_PROPERTY_REF(PROP_BACKGROUND_COLOR, BackgroundColor, backgroundColor, xColor, TextEntityItem::DEFAULT_BACKGROUND_COLOR); + DEFINE_PROPERTY_REF(PROP_TEXT_COLOR, TextColor, textColor, u8vec3Color, TextEntityItem::DEFAULT_TEXT_COLOR); + DEFINE_PROPERTY_REF(PROP_BACKGROUND_COLOR, BackgroundColor, backgroundColor, u8vec3Color, TextEntityItem::DEFAULT_BACKGROUND_COLOR); DEFINE_PROPERTY_REF_ENUM(PROP_SHAPE_TYPE, ShapeType, shapeType, ShapeType, SHAPE_TYPE_NONE); DEFINE_PROPERTY(PROP_MAX_PARTICLES, MaxParticles, maxParticles, quint32, particle::DEFAULT_MAX_PARTICLES); DEFINE_PROPERTY(PROP_LIFESPAN, Lifespan, lifespan, float, particle::DEFAULT_LIFESPAN); @@ -206,7 +209,7 @@ public: DEFINE_PROPERTY_GROUP(Animation, animation, AnimationPropertyGroup); DEFINE_PROPERTY_REF(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString, ""); DEFINE_PROPERTY(PROP_LINE_WIDTH, LineWidth, lineWidth, float, LineEntityItem::DEFAULT_LINE_WIDTH); - DEFINE_PROPERTY_REF(LINE_POINTS, LinePoints, linePoints, QVector, QVector()); + DEFINE_PROPERTY_REF(LINE_POINTS, LinePoints, linePoints, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); DEFINE_PROPERTY_REF(PROP_HREF, Href, href, QString, ""); DEFINE_PROPERTY_REF(PROP_DESCRIPTION, Description, description, QString, ""); DEFINE_PROPERTY(PROP_FACE_CAMERA, FaceCamera, faceCamera, bool, TextEntityItem::DEFAULT_FACE_CAMERA); @@ -233,8 +236,8 @@ public: DEFINE_PROPERTY_REF_ENUM(PROP_MATERIAL_MAPPING_MODE, MaterialMappingMode, materialMappingMode, MaterialMappingMode, UV); DEFINE_PROPERTY_REF(PROP_MATERIAL_PRIORITY, Priority, priority, quint16, 0); DEFINE_PROPERTY_REF(PROP_PARENT_MATERIAL_NAME, ParentMaterialName, parentMaterialName, QString, "0"); - DEFINE_PROPERTY_REF(PROP_MATERIAL_MAPPING_POS, MaterialMappingPos, materialMappingPos, vec2, glm::vec2(0, 0)); - DEFINE_PROPERTY_REF(PROP_MATERIAL_MAPPING_SCALE, MaterialMappingScale, materialMappingScale, vec2, glm::vec2(1, 1)); + DEFINE_PROPERTY_REF(PROP_MATERIAL_MAPPING_POS, MaterialMappingPos, materialMappingPos, glm::vec2, glm::vec2(0.0f)); + DEFINE_PROPERTY_REF(PROP_MATERIAL_MAPPING_SCALE, MaterialMappingScale, materialMappingScale, glm::vec2, glm::vec2(1.0f)); DEFINE_PROPERTY_REF(PROP_MATERIAL_MAPPING_ROT, MaterialMappingRot, materialMappingRot, float, 0); DEFINE_PROPERTY_REF(PROP_MATERIAL_DATA, MaterialData, materialData, QString, ""); @@ -260,16 +263,16 @@ public: DEFINE_PROPERTY_REF(PROP_STATIC_CERTIFICATE_VERSION, StaticCertificateVersion, staticCertificateVersion, quint32, ENTITY_ITEM_DEFAULT_STATIC_CERTIFICATE_VERSION); // these are used when bouncing location data into and out of scripts - DEFINE_PROPERTY_REF(PROP_LOCAL_POSITION, LocalPosition, localPosition, vec3, ENTITY_ITEM_ZERO_VEC3); + DEFINE_PROPERTY_REF(PROP_LOCAL_POSITION, LocalPosition, localPosition, glm::vec3, ENTITY_ITEM_ZERO_VEC3); DEFINE_PROPERTY_REF(PROP_LOCAL_ROTATION, LocalRotation, localRotation, quat, ENTITY_ITEM_DEFAULT_ROTATION); - DEFINE_PROPERTY_REF(PROP_LOCAL_VELOCITY, LocalVelocity, localVelocity, vec3, ENTITY_ITEM_ZERO_VEC3); - DEFINE_PROPERTY_REF(PROP_LOCAL_ANGULAR_VELOCITY, LocalAngularVelocity, localAngularVelocity, vec3, ENTITY_ITEM_ZERO_VEC3); - DEFINE_PROPERTY_REF(PROP_LOCAL_DIMENSIONS, LocalDimensions, localDimensions, vec3, ENTITY_ITEM_ZERO_VEC3); + DEFINE_PROPERTY_REF(PROP_LOCAL_VELOCITY, LocalVelocity, localVelocity, glm::vec3, ENTITY_ITEM_ZERO_VEC3); + DEFINE_PROPERTY_REF(PROP_LOCAL_ANGULAR_VELOCITY, LocalAngularVelocity, localAngularVelocity, glm::vec3, ENTITY_ITEM_ZERO_VEC3); + DEFINE_PROPERTY_REF(PROP_LOCAL_DIMENSIONS, LocalDimensions, localDimensions, glm::vec3, ENTITY_ITEM_ZERO_VEC3); DEFINE_PROPERTY_REF(PROP_JOINT_ROTATIONS_SET, JointRotationsSet, jointRotationsSet, QVector, QVector()); DEFINE_PROPERTY_REF(PROP_JOINT_ROTATIONS, JointRotations, jointRotations, QVector, QVector()); DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS_SET, JointTranslationsSet, jointTranslationsSet, QVector, QVector()); - DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector, QVector()); + DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); DEFINE_PROPERTY(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool, ZoneEntityItem::DEFAULT_FLYING_ALLOWED); DEFINE_PROPERTY(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool, ZoneEntityItem::DEFAULT_GHOSTING_ALLOWED); @@ -292,6 +295,8 @@ public: DEFINE_PROPERTY(PROP_CLONE_AVATAR_ENTITY, CloneAvatarEntity, cloneAvatarEntity, bool, ENTITY_ITEM_DEFAULT_CLONE_AVATAR_ENTITY); DEFINE_PROPERTY_REF(PROP_CLONE_ORIGIN_ID, CloneOriginID, cloneOriginID, QUuid, ENTITY_ITEM_DEFAULT_CLONE_ORIGIN_ID); + DEFINE_PROPERTY_GROUP(Grab, grab, GrabPropertyGroup); + static QString getComponentModeString(uint32_t mode); static QString getComponentModeAsString(uint32_t mode); @@ -344,13 +349,18 @@ public: void setCreated(QDateTime& v); - bool hasTerseUpdateChanges() const; + bool hasTransformOrVelocityChanges() const; bool hasMiscPhysicsChanges() const; + bool hasSimulationRestrictedChanges() const; + void copySimulationRestrictedProperties(const EntityItemPointer& entity); + void clearSimulationRestrictedProperties(); + void clearSimulationOwner(); void setSimulationOwner(const QUuid& id, uint8_t priority); void setSimulationOwner(const QByteArray& data); void setSimulationPriority(uint8_t priority) { _simulationOwner.setPriority(priority); } + uint8_t computeSimulationBidPriority() const; void setActionDataDirty() { _actionDataChanged = true; } diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index ff4fb39354..d3a2dc6cec 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -101,8 +101,11 @@ changedProperties += P; \ } -inline QScriptValue convertScriptValue(QScriptEngine* e, const glm::vec2& v) { return vec2toScriptValue(e, v); } -inline QScriptValue convertScriptValue(QScriptEngine* e, const glm::vec3& v) { return vec3toScriptValue(e, v); } +inline QScriptValue convertScriptValue(QScriptEngine* e, const glm::vec2& v) { return vec2ToScriptValue(e, v); } +inline QScriptValue convertScriptValue(QScriptEngine* e, const glm::vec3& v) { return vec3ToScriptValue(e, v); } +inline QScriptValue vec3Color_convertScriptValue(QScriptEngine* e, const glm::vec3& v) { return vec3ColorToScriptValue(e, v); } +inline QScriptValue convertScriptValue(QScriptEngine* e, const glm::u8vec3& v) { return u8vec3ToScriptValue(e, v); } +inline QScriptValue u8vec3Color_convertScriptValue(QScriptEngine* e, const glm::u8vec3& v) { return u8vec3ColorToScriptValue(e, v); } inline QScriptValue convertScriptValue(QScriptEngine* e, float v) { return QScriptValue(v); } inline QScriptValue convertScriptValue(QScriptEngine* e, int v) { return QScriptValue(v); } inline QScriptValue convertScriptValue(QScriptEngine* e, bool v) { return QScriptValue(v); } @@ -111,10 +114,10 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, quint32 v) { return QSc inline QScriptValue convertScriptValue(QScriptEngine* e, quint64 v) { return QScriptValue((qsreal)v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const QString& v) { return QScriptValue(v); } -inline QScriptValue convertScriptValue(QScriptEngine* e, const xColor& v) { return xColorToScriptValue(e, v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const glm::quat& v) { return quatToScriptValue(e, v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const QScriptValue& v) { return v; } inline QScriptValue convertScriptValue(QScriptEngine* e, const QVector& v) {return qVectorVec3ToScriptValue(e, v); } +inline QScriptValue qVectorVec3Color_convertScriptValue(QScriptEngine* e, const QVector& v) {return qVectorVec3ColorToScriptValue(e, v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const QVector& v) {return qVectorQuatToScriptValue(e, v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const QVector& v) {return qVectorBoolToScriptValue(e, v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const QVector& v) { return qVectorFloatToScriptValue(e, v); } @@ -128,8 +131,6 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, const EntityItemID& v) inline QScriptValue convertScriptValue(QScriptEngine* e, const AACube& v) { return aaCubeToScriptValue(e, v); } - - #define COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(X,G,g,P,p) \ if ((desiredProperties.isEmpty() || desiredProperties.getHasProperty(X)) && \ (!skipDefaults || defaultEntityProperties.get##G().get##P() != get##P())) { \ @@ -142,6 +143,18 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, const AACube& v) { retu properties.setProperty(#g, groupProperties); \ } +#define COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(X,G,g,P,p,T) \ + if ((desiredProperties.isEmpty() || desiredProperties.getHasProperty(X)) && \ + (!skipDefaults || defaultEntityProperties.get##G().get##P() != get##P())) { \ + QScriptValue groupProperties = properties.property(#g); \ + if (!groupProperties.isValid()) { \ + groupProperties = engine->newObject(); \ + } \ + QScriptValue V = T##_convertScriptValue(engine, get##P()); \ + groupProperties.setProperty(#p, V); \ + properties.setProperty(#g, groupProperties); \ + } + #define COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(X,G,g,P,p,M) \ if ((desiredProperties.isEmpty() || desiredProperties.getHasProperty(X)) && \ (!skipDefaults || defaultEntityProperties.get##G().get##P() != get##P())) { \ @@ -161,6 +174,13 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, const AACube& v) { retu properties.setProperty(#P, V); \ } +#define COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(p,P,T) \ + if ((_desiredProperties.isEmpty() || _desiredProperties.getHasProperty(p)) && \ + (!skipDefaults || defaultEntityProperties._##P != _##P)) { \ + QScriptValue V = T##_convertScriptValue(engine, _##P); \ + properties.setProperty(#P, V); \ + } + #define COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(P, G) \ properties.setProperty(#P, G); @@ -171,6 +191,13 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, const AACube& v) { retu properties.setProperty(#P, V); \ } +#define COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_TYPED(p, P, G, T) \ + if ((_desiredProperties.isEmpty() || _desiredProperties.getHasProperty(p)) && \ + (!skipDefaults || defaultEntityProperties._##P != _##P)) { \ + QScriptValue V = T##_convertScriptValue(engine, G); \ + properties.setProperty(#P, V); \ + } + // same as COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER but uses #X instead of #P in the setProperty() step #define COPY_PROXY_PROPERTY_TO_QSCRIPTVALUE_GETTER(p, P, X, G) \ if (((!psuedoPropertyFlagsButDesiredEmpty && _desiredProperties.isEmpty()) || _desiredProperties.getHasProperty(p)) && \ @@ -205,7 +232,6 @@ inline QUuid QUuid_convertFromScriptValue(const QScriptValue& v, bool& isValid) inline EntityItemID EntityItemID_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toUuid(); } - inline QDateTime QDateTime_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; auto result = QDateTime::fromString(v.toVariant().toString().trimmed(), Qt::ISODate); @@ -213,8 +239,6 @@ inline QDateTime QDateTime_convertFromScriptValue(const QScriptValue& v, bool& i return result; } - - inline QByteArray QByteArray_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; QString b64 = v.toVariant().toString().trimmed(); @@ -222,49 +246,31 @@ inline QByteArray QByteArray_convertFromScriptValue(const QScriptValue& v, bool& } inline glm::vec2 vec2_convertFromScriptValue(const QScriptValue& v, bool& isValid) { - isValid = false; /// assume it can't be converted - QScriptValue x = v.property("x"); - QScriptValue y = v.property("y"); - if (x.isValid() && y.isValid()) { - glm::vec4 newValue(0); - newValue.x = x.toVariant().toFloat(); - newValue.y = y.toVariant().toFloat(); - isValid = !glm::isnan(newValue.x) && - !glm::isnan(newValue.y); - if (isValid) { - return newValue; - } - } - return glm::vec2(0); + isValid = true; + glm::vec2 vec2; + vec2FromScriptValue(v, vec2); + return vec2; } inline glm::vec3 vec3_convertFromScriptValue(const QScriptValue& v, bool& isValid) { - isValid = false; /// assume it can't be converted - QScriptValue x = v.property("x"); - QScriptValue y = v.property("y"); - QScriptValue z = v.property("z"); - if (!x.isValid()) { - x = v.property("red"); - } - if (!y.isValid()) { - y = v.property("green"); - } - if (!z.isValid()) { - z = v.property("blue"); - } - if (x.isValid() && y.isValid() && z.isValid()) { - glm::vec3 newValue(0); - newValue.x = x.toVariant().toFloat(); - newValue.y = y.toVariant().toFloat(); - newValue.z = z.toVariant().toFloat(); - isValid = !glm::isnan(newValue.x) && - !glm::isnan(newValue.y) && - !glm::isnan(newValue.z); - if (isValid) { - return newValue; - } - } - return glm::vec3(0); + isValid = true; + glm::vec3 vec3; + vec3FromScriptValue(v, vec3); + return vec3; +} + +inline glm::vec3 vec3Color_convertFromScriptValue(const QScriptValue& v, bool& isValid) { + isValid = true; + glm::vec3 vec3; + vec3FromScriptValue(v, vec3); + return vec3; +} + +inline glm::u8vec3 u8vec3Color_convertFromScriptValue(const QScriptValue& v, bool& isValid) { + isValid = true; + glm::u8vec3 vec3; + u8vec3FromScriptValue(v, vec3); + return vec3; } inline AACube AACube_convertFromScriptValue(const QScriptValue& v, bool& isValid) { @@ -317,31 +323,6 @@ inline glm::quat quat_convertFromScriptValue(const QScriptValue& v, bool& isVali return glm::quat(); } -inline xColor xColor_convertFromScriptValue(const QScriptValue& v, bool& isValid) { - xColor newValue { 255, 255, 255 }; - isValid = false; /// assume it can't be converted - QScriptValue r = v.property("red"); - QScriptValue g = v.property("green"); - QScriptValue b = v.property("blue"); - if (!r.isValid()) { - r = v.property("x"); - } - if (!g.isValid()) { - g = v.property("y"); - } - if (!b.isValid()) { - b = v.property("z"); - } - if (r.isValid() && g.isValid() && b.isValid()) { - newValue.red = r.toVariant().toInt(); - newValue.green = g.toVariant().toInt(); - newValue.blue = b.toVariant().toInt(); - isValid = true; - } - return newValue; -} - - #define COPY_PROPERTY_IF_CHANGED(P) \ { \ if (other._##P##Changed) { \ diff --git a/libraries/entities/src/EntityPropertyFlags.cpp b/libraries/entities/src/EntityPropertyFlags.cpp new file mode 100644 index 0000000000..c077b153b8 --- /dev/null +++ b/libraries/entities/src/EntityPropertyFlags.cpp @@ -0,0 +1,207 @@ + +#include "EntityPropertyFlags.h" + + +QDebug& operator<<(QDebug& dbg, const EntityPropertyFlags& f) { + QString result = "[ "; + + result = f.getHasProperty(PROP_PAGED_PROPERTY) ? result + "pagedProperty " : result; + result = f.getHasProperty(PROP_CUSTOM_PROPERTIES_INCLUDED) ? result + "customPropertiesIncluded " : result; + result = f.getHasProperty(PROP_VISIBLE) ? result + "visible " : result; + result = f.getHasProperty(PROP_CAN_CAST_SHADOW) ? result + "canCastShadow " : result; + result = f.getHasProperty(PROP_POSITION) ? result + "position " : result; + result = f.getHasProperty(PROP_DIMENSIONS) ? result + "dimensions " : result; + result = f.getHasProperty(PROP_ROTATION) ? result + "rotation " : result; + result = f.getHasProperty(PROP_DENSITY) ? result + "density " : result; + result = f.getHasProperty(PROP_VELOCITY) ? result + "velocity " : result; + result = f.getHasProperty(PROP_GRAVITY) ? result + "gravity " : result; + result = f.getHasProperty(PROP_DAMPING) ? result + "damping " : result; + result = f.getHasProperty(PROP_LIFETIME) ? result + "lifetime " : result; + result = f.getHasProperty(PROP_SCRIPT) ? result + "script " : result; + result = f.getHasProperty(PROP_COLOR) ? result + "color " : result; + result = f.getHasProperty(PROP_MODEL_URL) ? result + "modelUrl " : result; + result = f.getHasProperty(PROP_ANIMATION_URL) ? result + "animationUrl " : result; + result = f.getHasProperty(PROP_ANIMATION_FPS) ? result + "animationFps " : result; + result = f.getHasProperty(PROP_ANIMATION_FRAME_INDEX) ? result + "animationFrameIndex " : result; + result = f.getHasProperty(PROP_ANIMATION_PLAYING) ? result + "animationPlaying " : result; + result = f.getHasProperty(PROP_ANIMATION_ALLOW_TRANSLATION) ? result + "animationAllowTranslation " : result; + result = f.getHasProperty(PROP_RELAY_PARENT_JOINTS) ? result + "relayParentJoints " : result; + result = f.getHasProperty(PROP_REGISTRATION_POINT) ? result + "registrationPoint " : result; + result = f.getHasProperty(PROP_ANGULAR_VELOCITY) ? result + "angularVelocity " : result; + result = f.getHasProperty(PROP_ANGULAR_DAMPING) ? result + "angularDamping " : result; + result = f.getHasProperty(PROP_COLLISIONLESS) ? result + "collisionless " : result; + result = f.getHasProperty(PROP_DYNAMIC) ? result + "dynamic " : result; + result = f.getHasProperty(PROP_IS_SPOTLIGHT) ? result + "isSpotlight " : result; + result = f.getHasProperty(PROP_DIFFUSE_COLOR) ? result + "diffuseColor " : result; + result = f.getHasProperty(PROP_AMBIENT_COLOR_UNUSED) ? result + "ambientColorUnused " : result; + result = f.getHasProperty(PROP_SPECULAR_COLOR_UNUSED) ? result + "specularColorUnused " : result; + result = f.getHasProperty(PROP_INTENSITY) ? result + "intensity " : result; + result = f.getHasProperty(PROP_LINEAR_ATTENUATION_UNUSED) ? result + "linearAttenuationUnused " : result; + result = f.getHasProperty(PROP_QUADRATIC_ATTENUATION_UNUSED) ? result + "quadraticAttenuationUnused " : result; + result = f.getHasProperty(PROP_EXPONENT) ? result + "exponent " : result; + result = f.getHasProperty(PROP_CUTOFF) ? result + "cutoff " : result; + result = f.getHasProperty(PROP_LOCKED) ? result + "locked " : result; + result = f.getHasProperty(PROP_TEXTURES) ? result + "textures " : result; + result = f.getHasProperty(PROP_ANIMATION_SETTINGS_UNUSED) ? result + "animationSettingsUnused " : result; + result = f.getHasProperty(PROP_USER_DATA) ? result + "userData " : result; + result = f.getHasProperty(PROP_SHAPE_TYPE) ? result + "shapeType " : result; + result = f.getHasProperty(PROP_MAX_PARTICLES) ? result + "maxParticles " : result; + result = f.getHasProperty(PROP_LIFESPAN) ? result + "lifespan " : result; + result = f.getHasProperty(PROP_EMIT_RATE) ? result + "emitRate " : result; + result = f.getHasProperty(PROP_EMIT_SPEED) ? result + "emitSpeed " : result; + result = f.getHasProperty(PROP_EMIT_STRENGTH) ? result + "emitStrength " : result; + result = f.getHasProperty(PROP_EMIT_ACCELERATION) ? result + "emitAcceleration " : result; + result = f.getHasProperty(PROP_PARTICLE_RADIUS) ? result + "particleRadius " : result; + result = f.getHasProperty(PROP_COMPOUND_SHAPE_URL) ? result + "compoundShapeUrl " : result; + result = f.getHasProperty(PROP_MARKETPLACE_ID) ? result + "marketplaceID " : result; + result = f.getHasProperty(PROP_ACCELERATION) ? result + "acceleration " : result; + result = f.getHasProperty(PROP_SIMULATION_OWNER) ? result + "simulationOwner " : result; + result = f.getHasProperty(PROP_NAME) ? result + "name " : result; + result = f.getHasProperty(PROP_COLLISION_SOUND_URL) ? result + "collisionSoundUrl " : result; + result = f.getHasProperty(PROP_RESTITUTION) ? result + "restitution " : result; + result = f.getHasProperty(PROP_FRICTION) ? result + "friction " : result; + result = f.getHasProperty(PROP_VOXEL_VOLUME_SIZE) ? result + "voxelVolumeSize " : result; + result = f.getHasProperty(PROP_VOXEL_DATA) ? result + "voxelData " : result; + result = f.getHasProperty(PROP_VOXEL_SURFACE_STYLE) ? result + "voxelSurfaceStyle " : result; + result = f.getHasProperty(PROP_LINE_WIDTH) ? result + "lineWidth " : result; + result = f.getHasProperty(PROP_LINE_POINTS) ? result + "linePoints " : result; + result = f.getHasProperty(PROP_HREF) ? result + "href " : result; + result = f.getHasProperty(PROP_DESCRIPTION) ? result + "description " : result; + result = f.getHasProperty(PROP_FACE_CAMERA) ? result + "faceCamera " : result; + result = f.getHasProperty(PROP_SCRIPT_TIMESTAMP) ? result + "scriptTimestamp " : result; + result = f.getHasProperty(PROP_ACTION_DATA) ? result + "actionData " : result; + result = f.getHasProperty(PROP_X_TEXTURE_URL) ? result + "xTextureUrl " : result; + result = f.getHasProperty(PROP_Y_TEXTURE_URL) ? result + "yTextureUrl " : result; + result = f.getHasProperty(PROP_Z_TEXTURE_URL) ? result + "zTextureUrl " : result; + result = f.getHasProperty(PROP_NORMALS) ? result + "normals " : result; + result = f.getHasProperty(PROP_STROKE_COLORS) ? result + "strokeColors " : result; + result = f.getHasProperty(PROP_STROKE_WIDTHS) ? result + "strokeWidths " : result; + result = f.getHasProperty(PROP_IS_UV_MODE_STRETCH) ? result + "isUvModeStretch " : result; + result = f.getHasProperty(PROP_SPEED_SPREAD) ? result + "speedSpread " : result; + result = f.getHasProperty(PROP_ACCELERATION_SPREAD) ? result + "accelerationSpread " : result; + result = f.getHasProperty(PROP_X_N_NEIGHBOR_ID) ? result + "xNNeighborID " : result; + result = f.getHasProperty(PROP_Y_N_NEIGHBOR_ID) ? result + "yNNeighborID " : result; + result = f.getHasProperty(PROP_Z_N_NEIGHBOR_ID) ? result + "zNNeighborID " : result; + result = f.getHasProperty(PROP_X_P_NEIGHBOR_ID) ? result + "xPNeighborID " : result; + result = f.getHasProperty(PROP_Y_P_NEIGHBOR_ID) ? result + "yPNeighborID " : result; + result = f.getHasProperty(PROP_Z_P_NEIGHBOR_ID) ? result + "zPNeighborID " : result; + result = f.getHasProperty(PROP_RADIUS_SPREAD) ? result + "radiusSpread " : result; + result = f.getHasProperty(PROP_RADIUS_START) ? result + "radiusStart " : result; + result = f.getHasProperty(PROP_RADIUS_FINISH) ? result + "radiusFinish " : result; + result = f.getHasProperty(PROP_ALPHA) ? result + "alpha " : result; + result = f.getHasProperty(PROP_COLOR_SPREAD) ? result + "colorSpread " : result; + result = f.getHasProperty(PROP_COLOR_START) ? result + "colorStart " : result; + result = f.getHasProperty(PROP_COLOR_FINISH) ? result + "colorFinish " : result; + result = f.getHasProperty(PROP_ALPHA_SPREAD) ? result + "alphaSpread " : result; + result = f.getHasProperty(PROP_ALPHA_START) ? result + "alphaStart " : result; + result = f.getHasProperty(PROP_ALPHA_FINISH) ? result + "alphaFinish " : result; + result = f.getHasProperty(PROP_EMIT_ORIENTATION) ? result + "emitOrientation " : result; + result = f.getHasProperty(PROP_EMIT_DIMENSIONS) ? result + "emitDimensions " : result; + result = f.getHasProperty(PROP_EMIT_RADIUS_START) ? result + "emitRadiusStart " : result; + result = f.getHasProperty(PROP_POLAR_START) ? result + "polarStart " : result; + result = f.getHasProperty(PROP_POLAR_FINISH) ? result + "polarFinish " : result; + result = f.getHasProperty(PROP_AZIMUTH_START) ? result + "azimuthStart " : result; + result = f.getHasProperty(PROP_AZIMUTH_FINISH) ? result + "azimuthFinish " : result; + result = f.getHasProperty(PROP_ANIMATION_LOOP) ? result + "animationLoop " : result; + result = f.getHasProperty(PROP_ANIMATION_FIRST_FRAME) ? result + "animationFirstFrame " : result; + result = f.getHasProperty(PROP_ANIMATION_LAST_FRAME) ? result + "animationLastFrame " : result; + result = f.getHasProperty(PROP_ANIMATION_HOLD) ? result + "animationHold " : result; + result = f.getHasProperty(PROP_ANIMATION_START_AUTOMATICALLY) ? result + "animationStartAutomatically " : result; + result = f.getHasProperty(PROP_EMITTER_SHOULD_TRAIL) ? result + "emitterShouldTrail " : result; + result = f.getHasProperty(PROP_PARENT_ID) ? result + "parentID " : result; + result = f.getHasProperty(PROP_PARENT_JOINT_INDEX) ? result + "parentJointIndex " : result; + result = f.getHasProperty(PROP_LOCAL_POSITION) ? result + "localPosition " : result; + result = f.getHasProperty(PROP_LOCAL_ROTATION) ? result + "localRotation " : result; + result = f.getHasProperty(PROP_QUERY_AA_CUBE) ? result + "queryAaCube " : result; + result = f.getHasProperty(PROP_JOINT_ROTATIONS_SET) ? result + "jointRotationsSet " : result; + result = f.getHasProperty(PROP_JOINT_ROTATIONS) ? result + "jointRotations " : result; + result = f.getHasProperty(PROP_JOINT_TRANSLATIONS_SET) ? result + "jointTranslationsSet " : result; + result = f.getHasProperty(PROP_JOINT_TRANSLATIONS) ? result + "jointTranslations " : result; + result = f.getHasProperty(PROP_COLLISION_MASK) ? result + "collisionMask " : result; + result = f.getHasProperty(PROP_FALLOFF_RADIUS) ? result + "falloffRadius " : result; + result = f.getHasProperty(PROP_FLYING_ALLOWED) ? result + "flyingAllowed " : result; + result = f.getHasProperty(PROP_GHOSTING_ALLOWED) ? result + "ghostingAllowed " : result; + result = f.getHasProperty(PROP_CLIENT_ONLY) ? result + "clientOnly " : result; + result = f.getHasProperty(PROP_OWNING_AVATAR_ID) ? result + "owningAvatarID " : result; + result = f.getHasProperty(PROP_SHAPE) ? result + "shape " : result; + result = f.getHasProperty(PROP_DPI) ? result + "dpi " : result; + result = f.getHasProperty(PROP_LOCAL_VELOCITY) ? result + "localVelocity " : result; + result = f.getHasProperty(PROP_LOCAL_ANGULAR_VELOCITY) ? result + "localAngularVelocity " : result; + result = f.getHasProperty(PROP_LAST_EDITED_BY) ? result + "lastEditedBy " : result; + result = f.getHasProperty(PROP_SERVER_SCRIPTS) ? result + "serverScripts " : result; + result = f.getHasProperty(PROP_FILTER_URL) ? result + "filterUrl " : result; + result = f.getHasProperty(PROP_ITEM_NAME) ? result + "itemName " : result; + result = f.getHasProperty(PROP_ITEM_DESCRIPTION) ? result + "itemDescription " : result; + result = f.getHasProperty(PROP_ITEM_CATEGORIES) ? result + "itemCategories " : result; + result = f.getHasProperty(PROP_ITEM_ARTIST) ? result + "itemArtist " : result; + result = f.getHasProperty(PROP_ITEM_LICENSE) ? result + "itemLicense " : result; + result = f.getHasProperty(PROP_LIMITED_RUN) ? result + "limitedRun " : result; + result = f.getHasProperty(PROP_EDITION_NUMBER) ? result + "editionNumber " : result; + result = f.getHasProperty(PROP_ENTITY_INSTANCE_NUMBER) ? result + "entityInstanceNumber " : result; + result = f.getHasProperty(PROP_CERTIFICATE_ID) ? result + "certificateID " : result; + result = f.getHasProperty(PROP_STATIC_CERTIFICATE_VERSION) ? result + "staticCertificateVersion " : result; + result = f.getHasProperty(PROP_CLONEABLE) ? result + "cloneable " : result; + result = f.getHasProperty(PROP_CLONE_LIFETIME) ? result + "cloneLifetime " : result; + result = f.getHasProperty(PROP_CLONE_LIMIT) ? result + "cloneLimit " : result; + result = f.getHasProperty(PROP_CLONE_DYNAMIC) ? result + "cloneDynamic " : result; + result = f.getHasProperty(PROP_CLONE_AVATAR_ENTITY) ? result + "cloneAvatarEntity " : result; + result = f.getHasProperty(PROP_CLONE_ORIGIN_ID) ? result + "cloneOriginID " : result; + result = f.getHasProperty(PROP_HAZE_MODE) ? result + "hazeMode " : result; + result = f.getHasProperty(PROP_KEYLIGHT_COLOR) ? result + "keylightColor " : result; + result = f.getHasProperty(PROP_KEYLIGHT_INTENSITY) ? result + "keylightIntensity " : result; + result = f.getHasProperty(PROP_KEYLIGHT_DIRECTION) ? result + "keylightDirection " : result; + result = f.getHasProperty(PROP_KEYLIGHT_CAST_SHADOW) ? result + "keylightCastShadow " : result; + result = f.getHasProperty(PROP_HAZE_RANGE) ? result + "hazeRange " : result; + result = f.getHasProperty(PROP_HAZE_COLOR) ? result + "hazeColor " : result; + result = f.getHasProperty(PROP_HAZE_GLARE_COLOR) ? result + "hazeGlareColor " : result; + result = f.getHasProperty(PROP_HAZE_ENABLE_GLARE) ? result + "hazeEnableGlare " : result; + result = f.getHasProperty(PROP_HAZE_GLARE_ANGLE) ? result + "hazeGlareAngle " : result; + result = f.getHasProperty(PROP_HAZE_ALTITUDE_EFFECT) ? result + "hazeAltitudeEffect " : result; + result = f.getHasProperty(PROP_HAZE_CEILING) ? result + "hazeCeiling " : result; + result = f.getHasProperty(PROP_HAZE_BASE_REF) ? result + "hazeBaseRef " : result; + result = f.getHasProperty(PROP_HAZE_BACKGROUND_BLEND) ? result + "hazeBackgroundBlend " : result; + result = f.getHasProperty(PROP_HAZE_ATTENUATE_KEYLIGHT) ? result + "hazeAttenuateKeylight " : result; + result = f.getHasProperty(PROP_HAZE_KEYLIGHT_RANGE) ? result + "hazeKeylightRange " : result; + result = f.getHasProperty(PROP_HAZE_KEYLIGHT_ALTITUDE) ? result + "hazeKeylightAltitude " : result; + result = f.getHasProperty(PROP_KEY_LIGHT_MODE) ? result + "keyLightMode " : result; + result = f.getHasProperty(PROP_AMBIENT_LIGHT_MODE) ? result + "ambientLightMode " : result; + result = f.getHasProperty(PROP_SKYBOX_MODE) ? result + "skyboxMode " : result; + result = f.getHasProperty(PROP_LOCAL_DIMENSIONS) ? result + "localDimensions " : result; + result = f.getHasProperty(PROP_MATERIAL_URL) ? result + "materialUrl " : result; + result = f.getHasProperty(PROP_MATERIAL_MAPPING_MODE) ? result + "materialMappingMode " : result; + result = f.getHasProperty(PROP_MATERIAL_PRIORITY) ? result + "materialPriority " : result; + result = f.getHasProperty(PROP_PARENT_MATERIAL_NAME) ? result + "parentMaterialName " : result; + result = f.getHasProperty(PROP_MATERIAL_MAPPING_POS) ? result + "materialMappingPos " : result; + result = f.getHasProperty(PROP_MATERIAL_MAPPING_SCALE) ? result + "materialMappingScale " : result; + result = f.getHasProperty(PROP_MATERIAL_MAPPING_ROT) ? result + "materialMappingRot " : result; + result = f.getHasProperty(PROP_MATERIAL_DATA) ? result + "materialData " : result; + result = f.getHasProperty(PROP_VISIBLE_IN_SECONDARY_CAMERA) ? result + "visibleInSecondaryCamera " : result; + result = f.getHasProperty(PROP_PARTICLE_SPIN) ? result + "particleSpin " : result; + result = f.getHasProperty(PROP_SPIN_START) ? result + "spinStart " : result; + result = f.getHasProperty(PROP_SPIN_FINISH) ? result + "spinFinish " : result; + result = f.getHasProperty(PROP_SPIN_SPREAD) ? result + "spinSpread " : result; + result = f.getHasProperty(PROP_PARTICLE_ROTATE_WITH_ENTITY) ? result + "particleRotateWithEntity " : result; + result = f.getHasProperty(PROP_BLOOM_INTENSITY) ? result + "bloomIntensity " : result; + result = f.getHasProperty(PROP_BLOOM_THRESHOLD) ? result + "bloomThreshold " : result; + result = f.getHasProperty(PROP_BLOOM_SIZE) ? result + "bloomSize " : result; + result = f.getHasProperty(PROP_GRAB_GRABBABLE) ? result + "grab.Grabbable " : result; + result = f.getHasProperty(PROP_GRAB_KINEMATIC) ? result + "grab.Kinematic " : result; + result = f.getHasProperty(PROP_GRAB_FOLLOWS_CONTROLLER) ? result + "grab.FollowsController " : result; + result = f.getHasProperty(PROP_GRAB_TRIGGERABLE) ? result + "grab.Triggerable " : result; + result = f.getHasProperty(PROP_GRAB_EQUIPPABLE) ? result + "grab.Equippable " : result; + result = + f.getHasProperty(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET) ? result + "grab.LeftEquippablePositionOffset " : result; + result = + f.getHasProperty(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET) ? result + "grab.LeftEquippableRotationOffset " : result; + result = + f.getHasProperty(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET) ? result + "grab.RightEquippablePositionOffset " : result; + result = + f.getHasProperty(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET) ? result + "grab.RightEquippableRotationOffset " : result; + result = f.getHasProperty(PROP_GRAB_EQUIPPABLE_INDICATOR_URL) ? result + "grab.EquippableIndicatorURL " : result; + result = f.getHasProperty(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE) ? result + "grab.EquippableIndicatorScale " : result; + result = f.getHasProperty(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET) ? result + "grab.EquippableIndicatorOffset " : result; + + result += "]"; + dbg.nospace() << result; + return dbg; +} diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 3932730661..d2f687fbd3 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -262,6 +262,19 @@ enum EntityPropertyList { PROP_BLOOM_THRESHOLD, PROP_BLOOM_SIZE, + PROP_GRAB_GRABBABLE, + PROP_GRAB_KINEMATIC, + PROP_GRAB_FOLLOWS_CONTROLLER, + PROP_GRAB_TRIGGERABLE, + PROP_GRAB_EQUIPPABLE, + PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, + PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, + PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, + PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, + PROP_GRAB_EQUIPPABLE_INDICATOR_URL, + PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, + PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, + //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line PROP_AFTER_LAST_ITEM, @@ -304,5 +317,10 @@ typedef PropertyFlags EntityPropertyFlags; // one greater than the last item property due to the enum's auto-incrementing. extern EntityPropertyList PROP_LAST_ITEM; +QString EntityPropertyFlagsToString(EntityPropertyFlags propertiesFlags); + + +QDebug& operator<<(QDebug& dbg, const EntityPropertyFlags& f); + #endif // hifi_EntityPropertyFlags_h diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 3ecec89573..f8b22fdbae 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -174,6 +174,7 @@ EntityItemProperties convertPropertiesToScriptSemantics(const EntityItemProperti } +// TODO: this method looks expensive and should take properties by reference, update it, and return void EntityItemProperties convertPropertiesFromScriptSemantics(const EntityItemProperties& scriptSideProperties, bool scalesWithParent) { // convert position and rotation properties from world-space to local, unless localPosition and localRotation @@ -242,13 +243,12 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties _activityTracking.addedEntityCount++; auto nodeList = DependencyManager::get(); - auto sessionID = nodeList->getSessionUUID(); + const auto sessionID = nodeList->getSessionUUID(); EntityItemProperties propertiesWithSimID = properties; if (clientOnly) { - const QUuid myNodeID = sessionID; propertiesWithSimID.setClientOnly(clientOnly); - propertiesWithSimID.setOwningAvatarID(myNodeID); + propertiesWithSimID.setOwningAvatarID(sessionID); } propertiesWithSimID.setLastEditedBy(sessionID); @@ -290,7 +290,7 @@ bool EntityScriptingInterface::addLocalEntityCopy(EntityItemProperties& properti entity->setLastBroadcast(usecTimestampNow()); // since we're creating this object we will immediately volunteer to own its simulation - entity->setScriptSimulationPriority(VOLUNTEER_SIMULATION_PRIORITY); + entity->upgradeScriptSimulationPriority(VOLUNTEER_SIMULATION_PRIORITY); properties.setLastEdited(entity->getLastEdited()); } else { qCDebug(entities) << "script failed to add new Entity to local Octree"; @@ -530,54 +530,86 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& _activityTracking.editedEntityCount++; - auto nodeList = DependencyManager::get(); - auto sessionID = nodeList->getSessionUUID(); + const auto sessionID = DependencyManager::get()->getSessionUUID(); EntityItemProperties properties = scriptSideProperties; - properties.setLastEditedBy(sessionID); EntityItemID entityID(id); if (!_entityTree) { + properties.setLastEditedBy(sessionID); queueEntityMessage(PacketType::EntityEdit, entityID, properties); return id; } - // If we have a local entity tree set, then also update it. - bool updatedEntity = false; - _entityTree->withWriteLock([&] { - EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); + EntityItemPointer entity(nullptr); + SimulationOwner simulationOwner; + _entityTree->withReadLock([&] { + // make a copy of entity for local logic outside of tree lock + entity = _entityTree->findEntityByEntityItemID(entityID); if (!entity) { return; } - if (entity->getClientOnly() && entity->getOwningAvatarID() != nodeList->getSessionUUID()) { + if (entity->getClientOnly() && entity->getOwningAvatarID() != sessionID) { // don't edit other avatar's avatarEntities + properties = EntityItemProperties(); return; } + // make a copy of simulationOwner for local logic outside of tree lock + simulationOwner = entity->getSimulationOwner(); + }); - if (scriptSideProperties.parentRelatedPropertyChanged()) { - // All of parentID, parentJointIndex, position, rotation are needed to make sense of any of them. - // If any of these changed, pull any missing properties from the entity. + if (entity) { + if (properties.hasSimulationRestrictedChanges()) { + if (_bidOnSimulationOwnership) { + // flag for simulation ownership, or upgrade existing ownership priority + // (actual bids for simulation ownership are sent by the PhysicalEntitySimulation) + entity->upgradeScriptSimulationPriority(properties.computeSimulationBidPriority()); + if (simulationOwner.getID() == sessionID) { + // we own the simulation --> copy ALL restricted properties + properties.copySimulationRestrictedProperties(entity); + } else { + // we don't own the simulation but think we would like to - if (!scriptSideProperties.parentIDChanged()) { - properties.setParentID(entity->getParentID()); - } - if (!scriptSideProperties.parentJointIndexChanged()) { - properties.setParentJointIndex(entity->getParentJointIndex()); - } - if (!scriptSideProperties.localPositionChanged() && !scriptSideProperties.positionChanged()) { - properties.setPosition(entity->getWorldPosition()); - } - if (!scriptSideProperties.localRotationChanged() && !scriptSideProperties.rotationChanged()) { - properties.setRotation(entity->getWorldOrientation()); - } - if (!scriptSideProperties.localDimensionsChanged() && !scriptSideProperties.dimensionsChanged()) { - properties.setDimensions(entity->getScaledDimensions()); + uint8_t desiredPriority = entity->getScriptSimulationPriority(); + if (desiredPriority < simulationOwner.getPriority()) { + // the priority at which we'd like to own it is not high enough + // --> assume failure and clear all restricted property changes + properties.clearSimulationRestrictedProperties(); + } else { + // the priority at which we'd like to own it is high enough to win. + // --> assume success and copy ALL restricted properties + properties.copySimulationRestrictedProperties(entity); + } + } + } else if (!simulationOwner.getID().isNull()) { + // someone owns this but not us + // clear restricted properties + properties.clearSimulationRestrictedProperties(); } + // clear the cached simulationPriority level + entity->upgradeScriptSimulationPriority(0); } + + // set these to make EntityItemProperties::getScalesWithParent() work correctly properties.setClientOnly(entity->getClientOnly()); properties.setOwningAvatarID(entity->getOwningAvatarID()); - properties = convertPropertiesFromScriptSemantics(properties, properties.getScalesWithParent()); + + // make sure the properties has a type, so that the encode can know which properties to include + properties.setType(entity->getType()); + } else if (_bidOnSimulationOwnership) { + // bail when simulation participants don't know about entity + return QUuid(); + } + // TODO: it is possible there is no remaining useful changes in properties and we should bail early. + // How to check for this cheaply? + + properties = convertPropertiesFromScriptSemantics(properties, properties.getScalesWithParent()); + properties.setLastEditedBy(sessionID); + + // done reading and modifying properties --> start write + bool updatedEntity = false; + _entityTree->withWriteLock([&] { updatedEntity = _entityTree->updateEntity(entityID, properties); }); @@ -590,63 +622,37 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& // return QUuid(); // } - bool entityFound { false }; + bool hasQueryAACubeRelatedChanges = properties.queryAACubeRelatedPropertyChanged(); + // done writing, send update _entityTree->withReadLock([&] { - EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); + // find the entity again: maybe it was removed since we last found it + entity = _entityTree->findEntityByEntityItemID(entityID); if (entity) { - entityFound = true; - // make sure the properties has a type, so that the encode can know which properties to include - properties.setType(entity->getType()); - bool hasTerseUpdateChanges = properties.hasTerseUpdateChanges(); - bool hasPhysicsChanges = properties.hasMiscPhysicsChanges() || hasTerseUpdateChanges; - if (_bidOnSimulationOwnership && hasPhysicsChanges) { - auto nodeList = DependencyManager::get(); - const QUuid myNodeID = nodeList->getSessionUUID(); + uint64_t now = usecTimestampNow(); + entity->setLastBroadcast(now); - if (entity->getSimulatorID() == myNodeID) { - // we think we already own the simulation, so make sure to send ALL TerseUpdate properties - if (hasTerseUpdateChanges) { - entity->getAllTerseUpdateProperties(properties); - } - // TODO: if we knew that ONLY TerseUpdate properties have changed in properties AND the object - // is dynamic AND it is active in the physics simulation then we could chose to NOT queue an update - // and instead let the physics simulation decide when to send a terse update. This would remove - // the "slide-no-rotate" glitch (and typical double-update) that we see during the "poke rolling - // balls" test. However, even if we solve this problem we still need to provide a "slerp the visible - // proxy toward the true physical position" feature to hide the final glitches in the remote watcher's - // simulation. - - if (entity->getSimulationPriority() < SCRIPT_POKE_SIMULATION_PRIORITY) { - // we re-assert our simulation ownership at a higher priority - properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY); - } - } else { - // we make a bid for simulation ownership - properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY); - entity->setScriptSimulationPriority(SCRIPT_POKE_SIMULATION_PRIORITY); - } - } - if (properties.queryAACubeRelatedPropertyChanged()) { + if (hasQueryAACubeRelatedChanges) { properties.setQueryAACube(entity->getQueryAACube()); - } - entity->setLastBroadcast(usecTimestampNow()); - properties.setLastEdited(entity->getLastEdited()); - // if we've moved an entity with children, check/update the queryAACube of all descendents and tell the server - // if they've changed. - entity->forEachDescendant([&](SpatiallyNestablePointer descendant) { - if (descendant->getNestableType() == NestableType::Entity) { - if (descendant->updateQueryAACube()) { - EntityItemPointer entityDescendant = std::static_pointer_cast(descendant); - EntityItemProperties newQueryCubeProperties; - newQueryCubeProperties.setQueryAACube(descendant->getQueryAACube()); - newQueryCubeProperties.setLastEdited(properties.getLastEdited()); - queueEntityMessage(PacketType::EntityEdit, descendant->getID(), newQueryCubeProperties); - entityDescendant->setLastBroadcast(usecTimestampNow()); + // if we've moved an entity with children, check/update the queryAACube of all descendents and tell the server + // if they've changed. + entity->forEachDescendant([&](SpatiallyNestablePointer descendant) { + if (descendant->getNestableType() == NestableType::Entity) { + if (descendant->updateQueryAACube()) { + EntityItemPointer entityDescendant = std::static_pointer_cast(descendant); + EntityItemProperties newQueryCubeProperties; + newQueryCubeProperties.setQueryAACube(descendant->getQueryAACube()); + newQueryCubeProperties.setLastEdited(properties.getLastEdited()); + queueEntityMessage(PacketType::EntityEdit, descendant->getID(), newQueryCubeProperties); + entityDescendant->setLastBroadcast(now); + } } - } - }); - } else { + }); + } + } + }); + if (!entity) { + if (hasQueryAACubeRelatedChanges) { // Sometimes ESS don't have the entity they are trying to edit in their local tree. In this case, // convertPropertiesFromScriptSemantics doesn't get called and local* edits will get dropped. // This is because, on the script side, "position" is in world frame, but in the network @@ -668,8 +674,6 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& properties.setDimensions(properties.getLocalDimensions()); } } - }); - if (!entityFound) { // we've made an edit to an entity we don't know about, or to a non-entity. If it's a known non-entity, // print a warning and don't send an edit packet to the entity-server. QSharedPointer parentFinder = DependencyManager::get(); @@ -1192,9 +1196,9 @@ QScriptValue RayToEntityIntersectionResultToScriptValue(QScriptEngine* engine, c obj.setProperty("distance", value.distance); obj.setProperty("face", boxFaceToString(value.face)); - QScriptValue intersection = vec3toScriptValue(engine, value.intersection); + QScriptValue intersection = vec3ToScriptValue(engine, value.intersection); obj.setProperty("intersection", intersection); - QScriptValue surfaceNormal = vec3toScriptValue(engine, value.surfaceNormal); + QScriptValue surfaceNormal = vec3ToScriptValue(engine, value.surfaceNormal); obj.setProperty("surfaceNormal", surfaceNormal); obj.setProperty("extraInfo", engine->toScriptValue(value.extraInfo)); return obj; @@ -1449,7 +1453,7 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString, } action->setIsMine(true); success = entity->addAction(simulation, action); - entity->setScriptSimulationPriority(SCRIPT_GRAB_SIMULATION_PRIORITY); + entity->upgradeScriptSimulationPriority(SCRIPT_GRAB_SIMULATION_PRIORITY); return false; // Physics will cause a packet to be sent, so don't send from here. }); if (success) { @@ -1465,7 +1469,7 @@ bool EntityScriptingInterface::updateAction(const QUuid& entityID, const QUuid& return actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) { bool success = entity->updateAction(simulation, actionID, arguments); if (success) { - entity->setScriptSimulationPriority(SCRIPT_GRAB_SIMULATION_PRIORITY); + entity->upgradeScriptSimulationPriority(SCRIPT_GRAB_SIMULATION_PRIORITY); } return success; }); @@ -1479,7 +1483,7 @@ bool EntityScriptingInterface::deleteAction(const QUuid& entityID, const QUuid& success = entity->removeAction(simulation, actionID); if (success) { // reduce from grab to poke - entity->setScriptSimulationPriority(SCRIPT_POKE_SIMULATION_PRIORITY); + entity->upgradeScriptSimulationPriority(SCRIPT_POKE_SIMULATION_PRIORITY); } return false; // Physics will cause a packet to be sent, so don't send from here. }); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index a7c88ddc7d..0b3b8abba2 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -668,7 +668,7 @@ void EntityTree::unhookChildAvatar(const EntityItemID entityID) { void EntityTree::cleanupCloneIDs(const EntityItemID& entityID) { EntityItemPointer entity = findEntityByEntityItemID(entityID); if (entity) { - // remove clone ID from it's clone origin's clone ID list if clone origin exists + // remove clone ID from its clone origin's clone ID list if clone origin exists const QUuid& cloneOriginID = entity->getCloneOriginID(); if (!cloneOriginID.isNull()) { EntityItemPointer cloneOrigin = findEntityByID(cloneOriginID); @@ -1933,6 +1933,14 @@ void EntityTree::fixupNeedsParentFixups() { } }); entity->locationChanged(true); + + // Update our parent's bounding box + bool success = false; + auto parent = entity->getParentPointer(success); + if (success && parent) { + parent->updateQueryAACube(); + } + entity->postParentFixup(); } else if (getIsServer() || _avatarIDs.contains(entity->getParentID())) { // this is a child of an avatar, which the entity server will never have @@ -2558,7 +2566,7 @@ bool EntityTree::readFromMap(QVariantMap& map) { if (needsConversion && (properties.getType() == EntityTypes::EntityType::Zone)) { // The legacy version had no keylight mode - this is set to on properties.setKeyLightMode(COMPONENT_MODE_ENABLED); - + // The ambient URL has been moved from "keyLight" to "ambientLight" if (entityMap.contains("keyLight")) { QVariantMap keyLightObject = entityMap["keyLight"].toMap(); @@ -2629,6 +2637,108 @@ bool EntityTree::readFromMap(QVariantMap& map) { } } + // convert old grab-related userData to new grab properties + if (contentVersion < (int)EntityVersion::GrabProperties) { + QJsonObject userData = QJsonDocument::fromJson(properties.getUserData().toUtf8()).object(); + QJsonObject grabbableKey = userData["grabbableKey"].toObject(); + QJsonValue wantsTrigger = grabbableKey["wantsTrigger"]; + + GrabPropertyGroup& grabProperties = properties.getGrab(); + + if (wantsTrigger.isBool()) { + grabProperties.setTriggerable(wantsTrigger.toBool()); + } + QJsonValue triggerable = grabbableKey["triggerable"]; + if (triggerable.isBool()) { + grabProperties.setTriggerable(triggerable.toBool()); + } + QJsonValue grabbable = grabbableKey["grabbable"]; + if (grabbable.isBool()) { + grabProperties.setGrabbable(grabbable.toBool()); + } + QJsonValue ignoreIK = grabbableKey["ignoreIK"]; + if (ignoreIK.isBool()) { + grabProperties.setGrabFollowsController(ignoreIK.toBool()); + } + QJsonValue kinematic = grabbableKey["kinematic"]; + if (kinematic.isBool()) { + grabProperties.setGrabKinematic(kinematic.toBool()); + } + + if (grabbableKey["spatialKey"].isObject()) { + QJsonObject spatialKey = grabbableKey["spatialKey"].toObject(); + grabProperties.setEquippable(true); + if (spatialKey["leftRelativePosition"].isObject()) { + grabProperties.setEquippableLeftPosition(qMapToVec3(spatialKey["leftRelativePosition"].toVariant())); + } + if (spatialKey["rightRelativePosition"].isObject()) { + grabProperties.setEquippableRightPosition(qMapToVec3(spatialKey["rightRelativePosition"].toVariant())); + } + if (spatialKey["relativeRotation"].isObject()) { + grabProperties.setEquippableLeftRotation(qMapToQuat(spatialKey["relativeRotation"].toVariant())); + grabProperties.setEquippableRightRotation(qMapToQuat(spatialKey["relativeRotation"].toVariant())); + } + } + + QJsonObject wearable = userData["wearable"].toObject(); + QJsonObject joints = wearable["joints"].toObject(); + if (joints["LeftHand"].isArray()) { + QJsonArray leftHand = joints["LeftHand"].toArray(); + if (leftHand.size() == 2) { + grabProperties.setEquippable(true); + grabProperties.setEquippableLeftPosition(qMapToVec3(leftHand[0].toVariant())); + grabProperties.setEquippableLeftRotation(qMapToQuat(leftHand[1].toVariant())); + } + } + if (joints["RightHand"].isArray()) { + QJsonArray rightHand = joints["RightHand"].toArray(); + if (rightHand.size() == 2) { + grabProperties.setEquippable(true); + grabProperties.setEquippableRightPosition(qMapToVec3(rightHand[0].toVariant())); + grabProperties.setEquippableRightRotation(qMapToQuat(rightHand[1].toVariant())); + } + } + + if (userData["equipHotspots"].isArray()) { + QJsonArray equipHotspots = userData["equipHotspots"].toArray(); + if (equipHotspots.size() > 0) { + // just take the first one + QJsonObject firstHotSpot = equipHotspots[0].toObject(); + QJsonObject joints = firstHotSpot["joints"].toObject(); + if (joints["LeftHand"].isArray()) { + QJsonArray leftHand = joints["LeftHand"].toArray(); + if (leftHand.size() == 2) { + grabProperties.setEquippable(true); + grabProperties.setEquippableLeftPosition(qMapToVec3(leftHand[0].toVariant())); + grabProperties.setEquippableLeftRotation(qMapToQuat(leftHand[1].toVariant())); + } + } + if (joints["RightHand"].isArray()) { + QJsonArray rightHand = joints["RightHand"].toArray(); + if (rightHand.size() == 2) { + grabProperties.setEquippable(true); + grabProperties.setEquippableRightPosition(qMapToVec3(rightHand[0].toVariant())); + grabProperties.setEquippableRightRotation(qMapToQuat(rightHand[1].toVariant())); + } + } + QJsonValue indicatorURL = firstHotSpot["modelURL"]; + if (indicatorURL.isString()) { + grabProperties.setEquippableIndicatorURL(indicatorURL.toString()); + } + QJsonValue indicatorScale = firstHotSpot["modelScale"]; + if (indicatorScale.isDouble()) { + grabProperties.setEquippableIndicatorScale(glm::vec3((float)indicatorScale.toDouble())); + } else if (indicatorScale.isObject()) { + grabProperties.setEquippableIndicatorScale(qMapToVec3(indicatorScale.toVariant())); + } + QJsonValue indicatorOffset = firstHotSpot["position"]; + if (indicatorOffset.isObject()) { + grabProperties.setEquippableIndicatorOffset(qMapToVec3(indicatorOffset.toVariant())); + } + } + } + } + // Zero out the spread values that were fixed in version ParticleEntityFix so they behave the same as before if (contentVersion < (int)EntityVersion::ParticleEntityFix) { properties.setRadiusSpread(0.0f); diff --git a/libraries/entities/src/GrabPropertyGroup.cpp b/libraries/entities/src/GrabPropertyGroup.cpp new file mode 100644 index 0000000000..c433043e31 --- /dev/null +++ b/libraries/entities/src/GrabPropertyGroup.cpp @@ -0,0 +1,303 @@ +// +// GrabPropertyGroup.h +// libraries/entities/src +// +// Created by Seth Alves on 2018-8-8. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "GrabPropertyGroup.h" + +#include + +#include "EntityItemProperties.h" +#include "EntityItemPropertiesMacros.h" + +void GrabPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, + QScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties) const { + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_GRABBABLE, Grab, grab, Grabbable, grabbable); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_KINEMATIC, Grab, grab, GrabKinematic, grabKinematic); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_FOLLOWS_CONTROLLER, Grab, grab, GrabFollowsController, grabFollowsController); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_TRIGGERABLE, Grab, grab, Triggerable, triggerable); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_EQUIPPABLE, Grab, grab, Equippable, equippable); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, Grab, grab, + EquippableLeftPosition, equippableLeftPosition); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, Grab, grab, + EquippableLeftRotation, equippableLeftRotation); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, Grab, grab, + EquippableRightPosition, equippableRightPosition); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, Grab, grab, + EquippableRightRotation, equippableRightRotation); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, Grab, grab, + EquippableIndicatorURL, equippableIndicatorURL); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, Grab, grab, + EquippableIndicatorScale, equippableIndicatorScale); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, Grab, grab, + EquippableIndicatorOffset, equippableIndicatorOffset); + +} + +void GrabPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, grabbable, bool, setGrabbable); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, grabKinematic, bool, setGrabKinematic); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, grabFollowsController, bool, setGrabFollowsController); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, triggerable, bool, setTriggerable); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, equippable, bool, setEquippable); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, equippableLeftPosition, vec3, setEquippableLeftPosition); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, equippableLeftRotation, quat, setEquippableLeftRotation); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, equippableRightPosition, vec3, setEquippableRightPosition); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(grab, equippableRightRotation, quat, setEquippableRightRotation); +} + +void GrabPropertyGroup::merge(const GrabPropertyGroup& other) { + COPY_PROPERTY_IF_CHANGED(grabbable); + COPY_PROPERTY_IF_CHANGED(grabKinematic); + COPY_PROPERTY_IF_CHANGED(grabFollowsController); + COPY_PROPERTY_IF_CHANGED(triggerable); + COPY_PROPERTY_IF_CHANGED(equippable); + COPY_PROPERTY_IF_CHANGED(equippableLeftPosition); + COPY_PROPERTY_IF_CHANGED(equippableLeftRotation); + COPY_PROPERTY_IF_CHANGED(equippableRightPosition); + COPY_PROPERTY_IF_CHANGED(equippableRightRotation); +} + +void GrabPropertyGroup::debugDump() const { + qCDebug(entities) << " GrabPropertyGroup: ---------------------------------------------"; + + qCDebug(entities) << " _grabbable:" << _grabbable; + qCDebug(entities) << " _grabKinematic:" << _grabKinematic; + qCDebug(entities) << " _grabFollowsController:" << _grabFollowsController; + qCDebug(entities) << " _triggerable:" << _triggerable; + qCDebug(entities) << " _equippable:" << _equippable; + qCDebug(entities) << " _equippableLeftPosition:" << _equippableLeftPosition; + qCDebug(entities) << " _equippableLeftRotation:" << _equippableLeftRotation; + qCDebug(entities) << " _equippableRightPosition:" << _equippableRightPosition; + qCDebug(entities) << " _equippableRightRotation:" << _equippableRightRotation; +} + +void GrabPropertyGroup::listChangedProperties(QList& out) { + if (grabbableChanged()) { + out << "grab-grabbable"; + } + if (grabKinematicChanged()) { + out << "grab-grabKinematic"; + } + if (grabFollowsControllerChanged()) { + out << "grab-followsController"; + } + if (triggerableChanged()) { + out << "grab-triggerable"; + } + if (equippableChanged()) { + out << "grab-equippable"; + } + if (equippableLeftPositionChanged()) { + out << "grab-equippableLeftPosition"; + } + if (equippableLeftRotationChanged()) { + out << "grab-equippableLeftRotation"; + } + if (equippableRightPositionChanged()) { + out << "grab-equippableRightPosition"; + } + if (equippableRightRotationChanged()) { + out << "grab-equippableRightRotation"; + } +} + +bool GrabPropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_GRAB_GRABBABLE, getGrabbable()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_KINEMATIC, getGrabKinematic()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_FOLLOWS_CONTROLLER, getGrabFollowsController()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_TRIGGERABLE, getTriggerable()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE, getEquippable()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, getEquippableLeftPosition()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, getEquippableLeftRotation()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, getEquippableRightPosition()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, getEquippableRightRotation()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, getEquippableIndicatorURL()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, getEquippableIndicatorScale()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, getEquippableIndicatorOffset()); + + return true; +} + +bool GrabPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, + const unsigned char*& dataAt , int& processedBytes) { + + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + + READ_ENTITY_PROPERTY(PROP_GRAB_GRABBABLE, bool, setGrabbable); + READ_ENTITY_PROPERTY(PROP_GRAB_KINEMATIC, bool, setGrabKinematic); + READ_ENTITY_PROPERTY(PROP_GRAB_FOLLOWS_CONTROLLER, bool, setGrabFollowsController); + READ_ENTITY_PROPERTY(PROP_GRAB_TRIGGERABLE, bool, setTriggerable); + READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE, bool, setEquippable); + READ_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, glm::vec3, setEquippableLeftPosition); + READ_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, glm::quat, setEquippableLeftRotation); + READ_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, glm::vec3, setEquippableRightPosition); + READ_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, glm::quat, setEquippableRightRotation); + READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, QString, setEquippableIndicatorURL); + READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, glm::vec3, setEquippableIndicatorScale); + READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, glm::vec3, setEquippableIndicatorOffset); + + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_GRABBABLE, Grabbable); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_KINEMATIC, GrabKinematic); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_FOLLOWS_CONTROLLER, GrabFollowsController); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_TRIGGERABLE, Triggerable); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_EQUIPPABLE, Equippable); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, EquippableLeftPosition); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, EquippableLeftRotation); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, EquippableRightPosition); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, EquippableRightRotation); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, EquippableIndicatorURL); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, EquippableIndicatorScale); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, EquippableIndicatorOffset); + + processedBytes += bytesRead; + + Q_UNUSED(somethingChanged); + + return true; +} + +void GrabPropertyGroup::markAllChanged() { + _grabbableChanged = true; + _grabKinematicChanged = true; + _grabFollowsControllerChanged = true; + _triggerableChanged = true; + _equippableChanged = true; + _equippableLeftPositionChanged = true; + _equippableLeftRotationChanged = true; + _equippableRightPositionChanged = true; + _equippableRightRotationChanged = true; +} + +EntityPropertyFlags GrabPropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + + CHECK_PROPERTY_CHANGE(PROP_GRAB_GRABBABLE, grabbable); + CHECK_PROPERTY_CHANGE(PROP_GRAB_KINEMATIC, grabKinematic); + CHECK_PROPERTY_CHANGE(PROP_GRAB_FOLLOWS_CONTROLLER, grabFollowsController); + CHECK_PROPERTY_CHANGE(PROP_GRAB_TRIGGERABLE, triggerable); + CHECK_PROPERTY_CHANGE(PROP_GRAB_EQUIPPABLE, equippable); + CHECK_PROPERTY_CHANGE(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, equippableLeftPosition); + CHECK_PROPERTY_CHANGE(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, equippableLeftRotation); + CHECK_PROPERTY_CHANGE(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, equippableRightPosition); + CHECK_PROPERTY_CHANGE(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, equippableRightRotation); + CHECK_PROPERTY_CHANGE(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, equippableIndicatorURL); + CHECK_PROPERTY_CHANGE(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, equippableIndicatorScale); + CHECK_PROPERTY_CHANGE(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, equippableIndicatorOffset); + + return changedProperties; +} + +void GrabPropertyGroup::getProperties(EntityItemProperties& properties) const { + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, Grabbable, getGrabbable); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, GrabKinematic, getGrabKinematic); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, GrabFollowsController, getGrabFollowsController); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, Triggerable, getTriggerable); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, Equippable, getEquippable); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, EquippableLeftPosition, getEquippableLeftPosition); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, EquippableLeftRotation, getEquippableLeftRotation); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, EquippableRightPosition, getEquippableRightPosition); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Grab, EquippableRightRotation, getEquippableRightRotation); +} + +bool GrabPropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, Grabbable, grabbable, setGrabbable); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, GrabKinematic, grabKinematic, setGrabKinematic); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, GrabFollowsController, grabFollowsController, setGrabFollowsController); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, Triggerable, triggerable, setTriggerable); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, Equippable, equippable, setEquippable); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, EquippableLeftPosition, equippableLeftPosition, setEquippableLeftPosition); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, EquippableLeftRotation, equippableLeftRotation, setEquippableLeftRotation); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, EquippableRightPosition, equippableRightPosition, + setEquippableRightPosition); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Grab, EquippableRightRotation, equippableRightRotation, + setEquippableRightRotation); + + return somethingChanged; +} + +EntityPropertyFlags GrabPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + + requestedProperties += PROP_GRAB_GRABBABLE; + requestedProperties += PROP_GRAB_KINEMATIC; + requestedProperties += PROP_GRAB_FOLLOWS_CONTROLLER; + requestedProperties += PROP_GRAB_TRIGGERABLE; + requestedProperties += PROP_GRAB_EQUIPPABLE; + requestedProperties += PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET; + requestedProperties += PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET; + requestedProperties += PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET; + requestedProperties += PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET; + requestedProperties += PROP_GRAB_EQUIPPABLE_INDICATOR_URL; + requestedProperties += PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE; + requestedProperties += PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET; + + return requestedProperties; +} + +void GrabPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_GRAB_GRABBABLE, getGrabbable()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_KINEMATIC, getGrabKinematic()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_FOLLOWS_CONTROLLER, getGrabFollowsController()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_TRIGGERABLE, getTriggerable()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE, getEquippable()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, getEquippableLeftPosition()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, getEquippableLeftRotation()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, getEquippableRightPosition()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, getEquippableRightRotation()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, getEquippableIndicatorURL()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, getEquippableIndicatorScale()); + APPEND_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, getEquippableIndicatorOffset()); +} + +int GrabPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + + READ_ENTITY_PROPERTY(PROP_GRAB_GRABBABLE, bool, setGrabbable); + READ_ENTITY_PROPERTY(PROP_GRAB_KINEMATIC, bool, setGrabKinematic); + READ_ENTITY_PROPERTY(PROP_GRAB_FOLLOWS_CONTROLLER, bool, setGrabFollowsController); + READ_ENTITY_PROPERTY(PROP_GRAB_TRIGGERABLE, bool, setTriggerable); + READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE, bool, setEquippable); + READ_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, glm::vec3, setEquippableLeftPosition); + READ_ENTITY_PROPERTY(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, glm::quat, setEquippableLeftRotation); + READ_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, glm::vec3, setEquippableRightPosition); + READ_ENTITY_PROPERTY(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, glm::quat, setEquippableRightRotation); + READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, QString, setEquippableIndicatorURL); + READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, glm::vec3, setEquippableIndicatorScale); + READ_ENTITY_PROPERTY(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, glm::vec3, setEquippableIndicatorOffset); + + return bytesRead; +} diff --git a/libraries/entities/src/GrabPropertyGroup.h b/libraries/entities/src/GrabPropertyGroup.h new file mode 100644 index 0000000000..37b157de06 --- /dev/null +++ b/libraries/entities/src/GrabPropertyGroup.h @@ -0,0 +1,141 @@ +// +// GrabPropertyGroup.h +// libraries/entities/src +// +// Created by Seth Alves on 2018-8-8. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_GrabPropertyGroup_h +#define hifi_GrabPropertyGroup_h + +#include + +#include + +#include + +#include "PropertyGroup.h" +#include "EntityItemPropertiesMacros.h" + +class EntityItemProperties; +class EncodeBitstreamParams; +class OctreePacketData; +class ReadBitstreamToTreeParams; + +static const bool INITIAL_GRABBABLE { true }; +static const bool INITIAL_KINEMATIC { true }; +static const bool INITIAL_FOLLOWS_CONTROLLER { true }; +static const bool INITIAL_TRIGGERABLE { false }; +static const bool INITIAL_EQUIPPABLE { false }; +static const glm::vec3 INITIAL_LEFT_EQUIPPABLE_POSITION { glm::vec3(0.0f) }; +static const glm::quat INITIAL_LEFT_EQUIPPABLE_ROTATION { glm::quat() }; +static const glm::vec3 INITIAL_RIGHT_EQUIPPABLE_POSITION { glm::vec3(0.0f) }; +static const glm::quat INITIAL_RIGHT_EQUIPPABLE_ROTATION { glm::quat() }; +static const glm::vec3 INITIAL_EQUIPPABLE_INDICATOR_SCALE { glm::vec3(1.0f) }; +static const glm::vec3 INITIAL_EQUIPPABLE_INDICATOR_OFFSET { glm::vec3(0.0f) }; + + +/**jsdoc + * Grab is defined by the following properties. + * @typedef {object} Entities.Grab + * + * @property {boolean} grabbable=true - If true the entity can be grabbed. + * @property {boolean} grabKinematic=true - If true the entity is updated in a kinematic manner. + * If false it will be grabbed using a tractor action. A kinematic grab will make the item appear more + * tightly held, but will cause it to behave poorly when interacting with dynamic entities. + * @property {boolean} grabFollowsController=true - If true the entity will follow the motions of the + * hand-controller even if the avatar's hand can't get to the implied position. This should be true + * for tools, pens, etc and false for things meant to decorate the hand. + * + * @property {boolean} triggerable=false - If true the entity will receive calls to trigger + * {@link Controller|Controller entity methods}. + * + * @property {boolean} equippable=true - If true the entity can be equipped. + * @property {Vec3} equippableLeftPosition=0,0,0 - Positional offset from the left hand, when equipped. + * @property {Quat} equippableLeftRotation=0,0,0,1 - Rotational offset from the left hand, when equipped. + * @property {Vec3} equippableRightPosition=0,0,0 - Positional offset from the right hand, when equipped. + * @property {Quat} equippableRightRotation=0,0,0,1 - Rotational offset from the right hand, when equipped. + * + * @property {string} equippableIndicatorURL="" - If non-empty, this model will be used to indicate that an + * entity is equippable, rather than the default. + * @property {Vec3} equippableIndicatorScale=1,1,1 - If equippableIndicatorURL is non-empty, this controls the + scale of the displayed overlay. + * @property {Vec3} equippableIndicatorOffset=0,0,0 - If equippableIndicatorURL is non-empty, this controls the + relative offset of the displayed overlay from the equippable entity. + */ + + +class GrabPropertyGroup : public PropertyGroup { +public: + // EntityItemProperty related helpers + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, + QScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties) const override; + virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override; + + void merge(const GrabPropertyGroup& other); + + virtual void debugDump() const override; + virtual void listChangedProperties(QList& out) override; + + virtual bool appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, + const unsigned char*& dataAt, int& processedBytes) override; + virtual void markAllChanged() override; + virtual EntityPropertyFlags getChangedProperties() const override; + + // EntityItem related helpers + // methods for getting/setting all properties of an entity + virtual void getProperties(EntityItemProperties& propertiesOut) const override; + + // returns true if something changed + virtual bool setProperties(const EntityItemProperties& properties) override; + + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) override; + + // grab properties + DEFINE_PROPERTY(PROP_GRAB_GRABBABLE, Grabbable, grabbable, bool, INITIAL_GRABBABLE); + DEFINE_PROPERTY(PROP_GRAB_KINEMATIC, GrabKinematic, grabKinematic, bool, INITIAL_KINEMATIC); + DEFINE_PROPERTY(PROP_GRAB_FOLLOWS_CONTROLLER, GrabFollowsController, grabFollowsController, bool, + INITIAL_FOLLOWS_CONTROLLER); + DEFINE_PROPERTY(PROP_GRAB_TRIGGERABLE, Triggerable, triggerable, bool, INITIAL_TRIGGERABLE); + DEFINE_PROPERTY(PROP_GRAB_EQUIPPABLE, Equippable, equippable, bool, INITIAL_EQUIPPABLE); + DEFINE_PROPERTY_REF(PROP_GRAB_LEFT_EQUIPPABLE_POSITION_OFFSET, EquippableLeftPosition, equippableLeftPosition, + glm::vec3, INITIAL_LEFT_EQUIPPABLE_POSITION); + DEFINE_PROPERTY_REF(PROP_GRAB_LEFT_EQUIPPABLE_ROTATION_OFFSET, EquippableLeftRotation, equippableLeftRotation, + glm::quat, INITIAL_LEFT_EQUIPPABLE_ROTATION); + DEFINE_PROPERTY_REF(PROP_GRAB_RIGHT_EQUIPPABLE_POSITION_OFFSET, EquippableRightPosition, equippableRightPosition, + glm::vec3, INITIAL_RIGHT_EQUIPPABLE_POSITION); + DEFINE_PROPERTY_REF(PROP_GRAB_RIGHT_EQUIPPABLE_ROTATION_OFFSET, EquippableRightRotation, equippableRightRotation, + glm::quat, INITIAL_RIGHT_EQUIPPABLE_ROTATION); + DEFINE_PROPERTY_REF(PROP_GRAB_EQUIPPABLE_INDICATOR_URL, EquippableIndicatorURL, equippableIndicatorURL, QString, ""); + DEFINE_PROPERTY_REF(PROP_GRAB_EQUIPPABLE_INDICATOR_SCALE, EquippableIndicatorScale, equippableIndicatorScale, + glm::vec3, INITIAL_EQUIPPABLE_INDICATOR_SCALE); + DEFINE_PROPERTY_REF(PROP_GRAB_EQUIPPABLE_INDICATOR_OFFSET, EquippableIndicatorOffset, equippableIndicatorOffset, + glm::vec3, INITIAL_EQUIPPABLE_INDICATOR_OFFSET); +}; + +#endif // hifi_GrabPropertyGroup_h diff --git a/libraries/entities/src/HazePropertyGroup.cpp b/libraries/entities/src/HazePropertyGroup.cpp index c15b28707c..632f73ced6 100644 --- a/libraries/entities/src/HazePropertyGroup.cpp +++ b/libraries/entities/src/HazePropertyGroup.cpp @@ -18,8 +18,8 @@ void HazePropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_RANGE, Haze, haze, HazeRange, hazeRange); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_COLOR, Haze, haze, HazeColor, hazeColor); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_GLARE_COLOR, Haze, haze, HazeGlareColor, hazeGlareColor); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_HAZE_COLOR, Haze, haze, HazeColor, hazeColor, u8vec3Color); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_HAZE_GLARE_COLOR, Haze, haze, HazeGlareColor, hazeGlareColor, u8vec3Color); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_ENABLE_GLARE, Haze, haze, HazeEnableGlare, hazeEnableGlare); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_HAZE_GLARE_ANGLE, Haze, haze, HazeGlareAngle, hazeGlareAngle); @@ -36,8 +36,8 @@ void HazePropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProp void HazePropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeRange, float, setHazeRange); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeColor, xColor, setHazeColor); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeGlareColor, xColor, setHazeGlareColor); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeColor, u8vec3Color, setHazeColor); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeGlareColor, u8vec3Color, setHazeGlareColor); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeEnableGlare, bool, setHazeEnableGlare); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(haze, hazeGlareAngle, float, setHazeGlareAngle); @@ -167,8 +167,8 @@ bool HazePropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, bool somethingChanged = false; READ_ENTITY_PROPERTY(PROP_HAZE_RANGE, float, setHazeRange); - READ_ENTITY_PROPERTY(PROP_HAZE_COLOR, xColor, setHazeColor); - READ_ENTITY_PROPERTY(PROP_HAZE_GLARE_COLOR, xColor, setHazeGlareColor); + READ_ENTITY_PROPERTY(PROP_HAZE_COLOR, u8vec3Color, setHazeColor); + READ_ENTITY_PROPERTY(PROP_HAZE_GLARE_COLOR, u8vec3Color, setHazeGlareColor); READ_ENTITY_PROPERTY(PROP_HAZE_ENABLE_GLARE, bool, setHazeEnableGlare); READ_ENTITY_PROPERTY(PROP_HAZE_GLARE_ANGLE, float, setHazeGlareAngle); @@ -343,8 +343,8 @@ int HazePropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* dat const unsigned char* dataAt = data; READ_ENTITY_PROPERTY(PROP_HAZE_RANGE, float, setHazeRange); - READ_ENTITY_PROPERTY(PROP_HAZE_COLOR, xColor, setHazeColor); - READ_ENTITY_PROPERTY(PROP_HAZE_GLARE_COLOR, xColor, setHazeGlareColor); + READ_ENTITY_PROPERTY(PROP_HAZE_COLOR, u8vec3Color, setHazeColor); + READ_ENTITY_PROPERTY(PROP_HAZE_GLARE_COLOR, u8vec3Color, setHazeGlareColor); READ_ENTITY_PROPERTY(PROP_HAZE_ENABLE_GLARE, bool, setHazeEnableGlare); READ_ENTITY_PROPERTY(PROP_HAZE_GLARE_ANGLE, float, setHazeGlareAngle); diff --git a/libraries/entities/src/HazePropertyGroup.h b/libraries/entities/src/HazePropertyGroup.h index e992aefbf3..595dbeaf51 100644 --- a/libraries/entities/src/HazePropertyGroup.h +++ b/libraries/entities/src/HazePropertyGroup.h @@ -28,8 +28,8 @@ class EntityTreeElementExtraEncodeData; class ReadBitstreamToTreeParams; static const float INITIAL_HAZE_RANGE{ 1000.0f }; -static const xColor initialHazeGlareColorXcolor{ 255, 229, 179 }; -static const xColor initialHazeColorXcolor{ 128, 154, 179 }; +static const glm::u8vec3 initialHazeGlareColor { 255, 229, 179 }; +static const glm::u8vec3 initialHazeColor { 128, 154, 179 }; static const float INITIAL_HAZE_GLARE_ANGLE{ 20.0f }; static const float INITIAL_HAZE_BASE_REFERENCE{ 0.0f }; @@ -118,8 +118,8 @@ public: // Range only parameters DEFINE_PROPERTY(PROP_HAZE_RANGE, HazeRange, hazeRange, float, INITIAL_HAZE_RANGE); - DEFINE_PROPERTY_REF(PROP_HAZE_COLOR, HazeColor, hazeColor, xColor, initialHazeColorXcolor); - DEFINE_PROPERTY_REF(PROP_HAZE_GLARE_COLOR, HazeGlareColor, hazeGlareColor, xColor, initialHazeGlareColorXcolor); + DEFINE_PROPERTY_REF(PROP_HAZE_COLOR, HazeColor, hazeColor, glm::u8vec3, initialHazeColor); + DEFINE_PROPERTY_REF(PROP_HAZE_GLARE_COLOR, HazeGlareColor, hazeGlareColor, glm::u8vec3, initialHazeGlareColor); DEFINE_PROPERTY(PROP_HAZE_ENABLE_GLARE, HazeEnableGlare, hazeEnableGlare, bool, false); DEFINE_PROPERTY_REF(PROP_HAZE_GLARE_ANGLE, HazeGlareAngle, hazeGlareAngle, float, INITIAL_HAZE_GLARE_ANGLE); diff --git a/libraries/entities/src/KeyLightPropertyGroup.cpp b/libraries/entities/src/KeyLightPropertyGroup.cpp index 67e6f05921..f0ad2965ce 100644 --- a/libraries/entities/src/KeyLightPropertyGroup.cpp +++ b/libraries/entities/src/KeyLightPropertyGroup.cpp @@ -17,7 +17,7 @@ #include "EntityItemProperties.h" #include "EntityItemPropertiesMacros.h" -const xColor KeyLightPropertyGroup::DEFAULT_KEYLIGHT_COLOR = { 255, 255, 255 }; +const glm::u8vec3 KeyLightPropertyGroup::DEFAULT_KEYLIGHT_COLOR = { 255, 255, 255 }; const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_INTENSITY = 1.0f; const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_AMBIENT_INTENSITY = 0.5f; const glm::vec3 KeyLightPropertyGroup::DEFAULT_KEYLIGHT_DIRECTION = { 0.0f, -1.0f, 0.0f }; @@ -26,20 +26,20 @@ const bool KeyLightPropertyGroup::DEFAULT_KEYLIGHT_CAST_SHADOWS { false }; void KeyLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_COLOR, KeyLight, keyLight, Color, color); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_KEYLIGHT_COLOR, KeyLight, keyLight, Color, color, u8vec3Color); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_INTENSITY, KeyLight, keyLight, Intensity, intensity); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_DIRECTION, KeyLight, keyLight, Direction, direction); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_CAST_SHADOW, KeyLight, keyLight, CastShadows, castShadows); } void KeyLightPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, color, xColor, setColor); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, color, u8vec3Color, setColor); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, intensity, float, setIntensity); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, direction, vec3, setDirection); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, castShadows, bool, setCastShadows); // legacy property support - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightColor, xColor, setColor, getColor); + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightColor, u8vec3Color, setColor, getColor); COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightIntensity, float, setIntensity, getIntensity); COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightDirection, vec3, setDirection, getDirection); COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightCastShadows, bool, setCastShadows, getCastShadows); @@ -99,7 +99,7 @@ bool KeyLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFl bool overwriteLocalData = true; bool somethingChanged = false; - READ_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, xColor, setColor); + READ_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, float, setIntensity); READ_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, glm::vec3, setDirection); READ_ENTITY_PROPERTY(PROP_KEYLIGHT_CAST_SHADOW, bool, setCastShadows); @@ -187,7 +187,7 @@ int KeyLightPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* int bytesRead = 0; const unsigned char* dataAt = data; - READ_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, xColor, setColor); + READ_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, float, setIntensity); READ_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, glm::vec3, setDirection); READ_ENTITY_PROPERTY(PROP_KEYLIGHT_CAST_SHADOW, bool, setCastShadows); diff --git a/libraries/entities/src/KeyLightPropertyGroup.h b/libraries/entities/src/KeyLightPropertyGroup.h index b966b78fc7..d7fa75a32e 100644 --- a/libraries/entities/src/KeyLightPropertyGroup.h +++ b/libraries/entities/src/KeyLightPropertyGroup.h @@ -84,13 +84,13 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; - static const xColor DEFAULT_KEYLIGHT_COLOR; + static const glm::u8vec3 DEFAULT_KEYLIGHT_COLOR; static const float DEFAULT_KEYLIGHT_INTENSITY; static const float DEFAULT_KEYLIGHT_AMBIENT_INTENSITY; static const glm::vec3 DEFAULT_KEYLIGHT_DIRECTION; static const bool DEFAULT_KEYLIGHT_CAST_SHADOWS; - DEFINE_PROPERTY_REF(PROP_KEYLIGHT_COLOR, Color, color, xColor, DEFAULT_KEYLIGHT_COLOR); + DEFINE_PROPERTY_REF(PROP_KEYLIGHT_COLOR, Color, color, glm::u8vec3, DEFAULT_KEYLIGHT_COLOR); DEFINE_PROPERTY(PROP_KEYLIGHT_INTENSITY, Intensity, intensity, float, DEFAULT_KEYLIGHT_INTENSITY); DEFINE_PROPERTY_REF(PROP_KEYLIGHT_DIRECTION, Direction, direction, glm::vec3, DEFAULT_KEYLIGHT_DIRECTION); DEFINE_PROPERTY(PROP_KEYLIGHT_CAST_SHADOW, CastShadows, castShadows, bool, DEFAULT_KEYLIGHT_CAST_SHADOWS); diff --git a/libraries/entities/src/LightEntityItem.cpp b/libraries/entities/src/LightEntityItem.cpp index 57a83a4173..e3de5f66f8 100644 --- a/libraries/entities/src/LightEntityItem.cpp +++ b/libraries/entities/src/LightEntityItem.cpp @@ -38,7 +38,6 @@ EntityItemPointer LightEntityItem::factory(const EntityItemID& entityID, const E // our non-pure virtual subclass for now... LightEntityItem::LightEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { _type = EntityTypes::Light; - _color[RED_INDEX] = _color[GREEN_INDEX] = _color[BLUE_INDEX] = 0; } void LightEntityItem::setUnscaledDimensions(const glm::vec3& value) { @@ -73,7 +72,7 @@ EntityItemProperties LightEntityItem::getProperties(const EntityPropertyFlags& d EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class COPY_ENTITY_PROPERTY_TO_PROPERTIES(isSpotlight, getIsSpotlight); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(intensity, getIntensity); COPY_ENTITY_PROPERTY_TO_PROPERTIES(exponent, getExponent); COPY_ENTITY_PROPERTY_TO_PROPERTIES(cutoff, getCutoff); @@ -176,7 +175,7 @@ int LightEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, const unsigned char* dataAt = data; READ_ENTITY_PROPERTY(PROP_IS_SPOTLIGHT, bool, setIsSpotlight); - READ_ENTITY_PROPERTY(PROP_COLOR, rgbColor, setColor); + READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor); READ_ENTITY_PROPERTY(PROP_INTENSITY, float, setIntensity); READ_ENTITY_PROPERTY(PROP_EXPONENT, float, setExponent); READ_ENTITY_PROPERTY(PROP_CUTOFF, float, setCutoff); @@ -214,26 +213,15 @@ void LightEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_FALLOFF_RADIUS, getFalloffRadius()); } -const rgbColor& LightEntityItem::getColor() const { - return _color; -} - -xColor LightEntityItem::getXColor() const { - xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; -} - -void LightEntityItem::setColor(const rgbColor& value) { - withWriteLock([&] { - memcpy(_color, value, sizeof(_color)); - _lightPropertiesChanged = true; +glm::u8vec3 LightEntityItem::getColor() const { + return resultWithReadLock([&] { + return _color; }); } -void LightEntityItem::setColor(const xColor& value) { +void LightEntityItem::setColor(const glm::u8vec3& value) { withWriteLock([&] { - _color[RED_INDEX] = value.red; - _color[GREEN_INDEX] = value.green; - _color[BLUE_INDEX] = value.blue; + _color = value; _lightPropertiesChanged = true; }); } diff --git a/libraries/entities/src/LightEntityItem.h b/libraries/entities/src/LightEntityItem.h index f56b5ce624..813333d534 100644 --- a/libraries/entities/src/LightEntityItem.h +++ b/libraries/entities/src/LightEntityItem.h @@ -52,18 +52,12 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; - const rgbColor& getColor() const; - xColor getXColor() const; - - void setColor(const rgbColor& value); - void setColor(const xColor& value); + glm::u8vec3 getColor() const; + void setColor(const glm::u8vec3& value); bool getIsSpotlight() const; void setIsSpotlight(bool value); - void setIgnoredColor(const rgbColor& value) { } - void setIgnoredAttenuation(float value) { } - float getIntensity() const; void setIntensity(float value); float getFalloffRadius() const; @@ -96,7 +90,7 @@ public: private: // properties of a light - rgbColor _color; + glm::u8vec3 _color; bool _isSpotlight { DEFAULT_IS_SPOTLIGHT }; float _intensity { DEFAULT_INTENSITY }; float _falloffRadius { DEFAULT_FALLOFF_RADIUS }; diff --git a/libraries/entities/src/LineEntityItem.cpp b/libraries/entities/src/LineEntityItem.cpp index 1b20922b7d..4957c30112 100644 --- a/libraries/entities/src/LineEntityItem.cpp +++ b/libraries/entities/src/LineEntityItem.cpp @@ -41,13 +41,8 @@ EntityItemProperties LineEntityItem::getProperties(const EntityPropertyFlags& de EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - - properties._color = getXColor(); - properties._colorChanged = false; - - + COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(lineWidth, getLineWidth); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(linePoints, getLinePoints); return properties; @@ -56,12 +51,11 @@ EntityItemProperties LineEntityItem::getProperties(const EntityPropertyFlags& de bool LineEntityItem::setProperties(const EntityItemProperties& properties) { bool somethingChanged = false; somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class - + SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor); SET_ENTITY_PROPERTY_FROM_PROPERTIES(lineWidth, setLineWidth); SET_ENTITY_PROPERTY_FROM_PROPERTIES(linePoints, setLinePoints); - if (somethingChanged) { bool wantDebug = false; if (wantDebug) { @@ -120,7 +114,7 @@ int LineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesRead = 0; const unsigned char* dataAt = data; - READ_ENTITY_PROPERTY(PROP_COLOR, rgbColor, setColor); + READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor); READ_ENTITY_PROPERTY(PROP_LINE_WIDTH, float, setLineWidth); READ_ENTITY_PROPERTY(PROP_LINE_POINTS, QVector, setLinePoints); @@ -154,36 +148,21 @@ void LineEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits void LineEntityItem::debugDump() const { quint64 now = usecTimestampNow(); qCDebug(entities) << " LINE EntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " color:" << _color[0] << "," << _color[1] << "," << _color[2]; + qCDebug(entities) << " color:" << _color; qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); } - -const rgbColor& LineEntityItem::getColor() const { - return _color; -} - -xColor LineEntityItem::getXColor() const { - xColor result; - withReadLock([&] { - result = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; - }); - return result; -} - -void LineEntityItem::setColor(const rgbColor& value) { - withWriteLock([&] { - memcpy(_color, value, sizeof(_color)); +glm::u8vec3 LineEntityItem::getColor() const { + return resultWithReadLock([&] { + return _color; }); } -void LineEntityItem::setColor(const xColor& value) { +void LineEntityItem::setColor(const glm::u8vec3& value) { withWriteLock([&] { - _color[RED_INDEX] = value.red; - _color[GREEN_INDEX] = value.green; - _color[BLUE_INDEX] = value.blue; + _color = value; }); } diff --git a/libraries/entities/src/LineEntityItem.h b/libraries/entities/src/LineEntityItem.h index 06f7830e06..1ebb248d23 100644 --- a/libraries/entities/src/LineEntityItem.h +++ b/libraries/entities/src/LineEntityItem.h @@ -41,11 +41,8 @@ class LineEntityItem : public EntityItem { EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; - const rgbColor& getColor() const; - xColor getXColor() const; - - void setColor(const rgbColor& value); - void setColor(const xColor& value); + glm::u8vec3 getColor() const; + void setColor(const glm::u8vec3& value); void setLineWidth(float lineWidth); float getLineWidth() const; @@ -76,7 +73,7 @@ class LineEntityItem : public EntityItem { static const int MAX_POINTS_PER_LINE; private: - rgbColor _color; + glm::u8vec3 _color; float _lineWidth { DEFAULT_LINE_WIDTH }; QVector _points; bool _pointsChanged { true }; diff --git a/libraries/entities/src/MaterialEntityItem.cpp b/libraries/entities/src/MaterialEntityItem.cpp index 06afda4283..825dd83348 100644 --- a/libraries/entities/src/MaterialEntityItem.cpp +++ b/libraries/entities/src/MaterialEntityItem.cpp @@ -290,9 +290,9 @@ void MaterialEntityItem::applyMaterial() { return; } Transform textureTransform; - textureTransform.setTranslation(glm::vec3(_materialMappingPos, 0)); - textureTransform.setRotation(glm::vec3(0, 0, glm::radians(_materialMappingRot))); - textureTransform.setScale(glm::vec3(_materialMappingScale, 1)); + textureTransform.setTranslation(glm::vec3(_materialMappingPos, 0.0f)); + textureTransform.setRotation(glm::vec3(0.0f, 0.0f, glm::radians(_materialMappingRot))); + textureTransform.setScale(glm::vec3(_materialMappingScale, 1.0f)); material->setTextureTransforms(textureTransform); graphics::MaterialLayer materialLayer = graphics::MaterialLayer(material, getPriority()); @@ -314,8 +314,25 @@ void MaterialEntityItem::applyMaterial() { _retryApply = true; } +AACube MaterialEntityItem::calculateInitialQueryAACube(bool& success) { + AACube aaCube = EntityItem::calculateInitialQueryAACube(success); + // A Material entity's queryAACube contains its parent's queryAACube + auto parent = getParentPointer(success); + if (success && parent) { + success = false; + AACube parentQueryAACube = parent->calculateInitialQueryAACube(success); + if (success) { + aaCube += parentQueryAACube.getMinimumPoint(); + aaCube += parentQueryAACube.getMaximumPoint(); + } + } + return aaCube; +} + void MaterialEntityItem::postParentFixup() { removeMaterial(); + _queryAACubeSet = false; // force an update so we contain our parent + updateQueryAACube(); applyMaterial(); } diff --git a/libraries/entities/src/MaterialEntityItem.h b/libraries/entities/src/MaterialEntityItem.h index 7177aaf718..8aaa833db9 100644 --- a/libraries/entities/src/MaterialEntityItem.h +++ b/libraries/entities/src/MaterialEntityItem.h @@ -85,6 +85,8 @@ public: void postParentFixup() override; + AACube calculateInitialQueryAACube(bool& success) override; + private: // URL for this material. Currently, only JSON format is supported. Set to "materialData" to use the material data to live edit a material. // The following fields are supported in the JSON: diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index ec864aa8c8..72192c8702 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -39,7 +39,6 @@ ModelEntityItem::ModelEntityItem(const EntityItemID& entityItemID) : EntityItem( // set the last animated when interface (re)starts _type = EntityTypes::Model; _lastKnownCurrentFrame = -1; - _color[0] = _color[1] = _color[2] = 0; _visuallyReady = false; } @@ -56,7 +55,7 @@ void ModelEntityItem::setTextures(const QString& textures) { EntityItemProperties ModelEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(modelURL, getModelURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(compoundShapeURL, getCompoundShapeURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures); @@ -117,7 +116,7 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, const unsigned char* dataAt = data; bool animationPropertiesChanged = false; - READ_ENTITY_PROPERTY(PROP_COLOR, rgbColor, setColor); + READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor); READ_ENTITY_PROPERTY(PROP_MODEL_URL, QString, setModelURL); READ_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures); @@ -154,6 +153,7 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + requestedProperties += PROP_COLOR; requestedProperties += PROP_MODEL_URL; requestedProperties += PROP_COMPOUND_SHAPE_URL; requestedProperties += PROP_TEXTURES; @@ -520,9 +520,6 @@ QVector ModelEntityItem::getJointTranslationsSet() const { } -xColor ModelEntityItem::getXColor() const { - xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; -} bool ModelEntityItem::hasModel() const { return resultWithReadLock([&] { return !_modelURL.isEmpty(); @@ -558,17 +555,15 @@ QString ModelEntityItem::getCollisionShapeURL() const { return getShapeType() == SHAPE_TYPE_COMPOUND ? getCompoundShapeURL() : getModelURL(); } -void ModelEntityItem::setColor(const rgbColor& value) { +void ModelEntityItem::setColor(const glm::u8vec3& value) { withWriteLock([&] { - memcpy(_color, value, sizeof(_color)); + _color = value; }); } -void ModelEntityItem::setColor(const xColor& value) { - withWriteLock([&] { - _color[RED_INDEX] = value.red; - _color[GREEN_INDEX] = value.green; - _color[BLUE_INDEX] = value.blue; +glm::u8vec3 ModelEntityItem::getColor() const { + return resultWithReadLock([&] { + return _color; }); } diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 2716097c93..91d2d81b88 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -55,12 +55,11 @@ public: void setShapeType(ShapeType type) override; virtual ShapeType getShapeType() const override; - // TODO: Move these to subclasses, or other appropriate abstraction // getters/setters applicable to models and particles + glm::u8vec3 getColor() const; + void setColor(const glm::u8vec3& value); - const rgbColor& getColor() const { return _color; } - xColor getXColor() const; bool hasModel() const; virtual bool hasCompoundShapeURL() const; @@ -73,9 +72,6 @@ public: // Returns the URL used for the collision shape QString getCollisionShapeURL() const; - void setColor(const rgbColor& value); - void setColor(const xColor& value); - // model related properties virtual void setModelURL(const QString& url); virtual void setCompoundShapeURL(const QString& url); @@ -160,7 +156,7 @@ protected: QVector _localJointData; int _lastKnownCurrentFrame{-1}; - rgbColor _color; + glm::u8vec3 _color; QString _modelURL; bool _relayParentJoints; diff --git a/libraries/entities/src/ParticleEffectEntityItem.cpp b/libraries/entities/src/ParticleEffectEntityItem.cpp index cea3e94ef0..46dcd1b006 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.cpp +++ b/libraries/entities/src/ParticleEffectEntityItem.cpp @@ -151,9 +151,6 @@ uint64_t Properties::emitIntervalUsecs() const { return 0; } -const xColor ParticleEffectEntityItem::DEFAULT_XCOLOR = xColor(static_cast(DEFAULT_COLOR.r), static_cast(DEFAULT_COLOR.g), static_cast(DEFAULT_COLOR.b)); -const xColor ParticleEffectEntityItem::DEFAULT_XCOLOR_SPREAD = xColor(static_cast(DEFAULT_COLOR_SPREAD.r), static_cast(DEFAULT_COLOR_SPREAD.g), static_cast(DEFAULT_COLOR_SPREAD.b)); - EntityItemPointer ParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer entity(new ParticleEffectEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); entity->setProperties(properties); @@ -165,7 +162,6 @@ ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityIte EntityItem(entityItemID) { _type = EntityTypes::ParticleEffect; - setColor(DEFAULT_COLOR); _visuallyReady = false; } @@ -412,7 +408,7 @@ void ParticleEffectEntityItem::computeAndUpdateDimensions() { EntityItemProperties ParticleEffectEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha); COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType); // FIXME - this doesn't appear to get used COPY_ENTITY_PROPERTY_TO_PROPERTIES(maxParticles, getMaxParticles); @@ -503,28 +499,12 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert return somethingChanged; } -void ParticleEffectEntityItem::setColor(const vec3& value) { +void ParticleEffectEntityItem::setColor(const glm::u8vec3& value) { withWriteLock([&] { _particleProperties.color.gradient.target = value; }); } -void ParticleEffectEntityItem::setColor(const xColor& value) { - withWriteLock([&] { - _particleProperties.color.gradient.target.r = value.red; - _particleProperties.color.gradient.target.g = value.green; - _particleProperties.color.gradient.target.b = value.blue; - }); -} - -xColor ParticleEffectEntityItem::getXColor() const { - xColor color; - color.red = _particleProperties.color.gradient.target.r; - color.green = _particleProperties.color.gradient.target.g; - color.blue = _particleProperties.color.gradient.target.b; - return color; -} - int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, @@ -533,15 +513,15 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch int bytesRead = 0; const unsigned char* dataAt = data; - READ_ENTITY_PROPERTY(PROP_COLOR, xColor, setColor); + READ_ENTITY_PROPERTY(PROP_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY(PROP_EMITTING_PARTICLES, bool, setIsEmitting); READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType); READ_ENTITY_PROPERTY(PROP_MAX_PARTICLES, quint32, setMaxParticles); READ_ENTITY_PROPERTY(PROP_LIFESPAN, float, setLifespan); READ_ENTITY_PROPERTY(PROP_EMIT_RATE, float, setEmitRate); - READ_ENTITY_PROPERTY(PROP_EMIT_ACCELERATION, vec3, setEmitAcceleration); - READ_ENTITY_PROPERTY(PROP_ACCELERATION_SPREAD, vec3, setAccelerationSpread); + READ_ENTITY_PROPERTY(PROP_EMIT_ACCELERATION, glm::vec3, setEmitAcceleration); + READ_ENTITY_PROPERTY(PROP_ACCELERATION_SPREAD, glm::vec3, setAccelerationSpread); READ_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, float, setParticleRadius); READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures); @@ -549,9 +529,9 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch READ_ENTITY_PROPERTY(PROP_RADIUS_START, float, setRadiusStart); READ_ENTITY_PROPERTY(PROP_RADIUS_FINISH, float, setRadiusFinish); - READ_ENTITY_PROPERTY(PROP_COLOR_SPREAD, xColor, setColorSpread); - READ_ENTITY_PROPERTY(PROP_COLOR_START, vec3, setColorStart); - READ_ENTITY_PROPERTY(PROP_COLOR_FINISH, vec3, setColorFinish); + READ_ENTITY_PROPERTY(PROP_COLOR_SPREAD, u8vec3Color, setColorSpread); + READ_ENTITY_PROPERTY(PROP_COLOR_START, vec3Color, setColorStart); + READ_ENTITY_PROPERTY(PROP_COLOR_FINISH, vec3Color, setColorFinish); READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha); READ_ENTITY_PROPERTY(PROP_ALPHA_SPREAD, float, setAlphaSpread); READ_ENTITY_PROPERTY(PROP_ALPHA_START, float, setAlphaStart); @@ -560,7 +540,7 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch READ_ENTITY_PROPERTY(PROP_EMIT_SPEED, float, setEmitSpeed); READ_ENTITY_PROPERTY(PROP_SPEED_SPREAD, float, setSpeedSpread); READ_ENTITY_PROPERTY(PROP_EMIT_ORIENTATION, quat, setEmitOrientation); - READ_ENTITY_PROPERTY(PROP_EMIT_DIMENSIONS, vec3, setEmitDimensions); + READ_ENTITY_PROPERTY(PROP_EMIT_DIMENSIONS, glm::vec3, setEmitDimensions); READ_ENTITY_PROPERTY(PROP_EMIT_RADIUS_START, float, setEmitRadiusStart); READ_ENTITY_PROPERTY(PROP_POLAR_START, float, setPolarStart); READ_ENTITY_PROPERTY(PROP_POLAR_FINISH, float, setPolarFinish); @@ -629,7 +609,7 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData, OctreeElement::AppendState& appendState) const { bool successPropertyFits = true; - APPEND_ENTITY_PROPERTY(PROP_COLOR, getXColor()); + APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor()); APPEND_ENTITY_PROPERTY(PROP_EMITTING_PARTICLES, getIsEmitting()); APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)getShapeType()); APPEND_ENTITY_PROPERTY(PROP_MAX_PARTICLES, getMaxParticles()); @@ -713,22 +693,12 @@ void ParticleEffectEntityItem::setColorFinish(const vec3& colorFinish) { }); } -void ParticleEffectEntityItem::setColorSpread(const xColor& value) { +void ParticleEffectEntityItem::setColorSpread(const glm::u8vec3& value) { withWriteLock([&] { - _particleProperties.color.gradient.spread.r = value.red; - _particleProperties.color.gradient.spread.g = value.green; - _particleProperties.color.gradient.spread.b = value.blue; + _particleProperties.color.gradient.spread = value; }); } -xColor ParticleEffectEntityItem::getColorSpread() const { - xColor color; - color.red = _particleProperties.color.gradient.spread.r; - color.green = _particleProperties.color.gradient.spread.g; - color.blue = _particleProperties.color.gradient.spread.b; - return color; -} - void ParticleEffectEntityItem::setEmitterShouldTrail(bool emitterShouldTrail) { withWriteLock([&] { _particleProperties.emission.shouldTrail = emitterShouldTrail; diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index a8e133942e..a89d7afc06 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -20,9 +20,9 @@ namespace particle { static const float SCRIPT_MAXIMUM_PI = 3.1416f; // Round up so that reasonable property values work static const float UNINITIALIZED = NAN; - static const vec3 DEFAULT_COLOR = { 255, 255, 255 }; + static const u8vec3 DEFAULT_COLOR = { 255, 255, 255 }; static const vec3 DEFAULT_COLOR_UNINITIALIZED = { UNINITIALIZED, UNINITIALIZED, UNINITIALIZED }; - static const vec3 DEFAULT_COLOR_SPREAD = { 0, 0, 0 }; + static const u8vec3 DEFAULT_COLOR_SPREAD = { 0, 0, 0 }; static const float DEFAULT_ALPHA = 1.0f; static const float DEFAULT_ALPHA_SPREAD = 0.0f; static const float DEFAULT_ALPHA_START = UNINITIALIZED; @@ -42,8 +42,8 @@ namespace particle { static const float MINIMUM_EMIT_SPEED = -1000.0f; static const float MAXIMUM_EMIT_SPEED = 1000.0f; // Approx mach 3 static const float DEFAULT_SPEED_SPREAD = 1.0f; - static const glm::quat DEFAULT_EMIT_ORIENTATION = glm::angleAxis(-PI_OVER_TWO, Vectors::UNIT_X); // Vertical - static const glm::vec3 DEFAULT_EMIT_DIMENSIONS = Vectors::ZERO; // Emit from point + static const quat DEFAULT_EMIT_ORIENTATION = glm::angleAxis(-PI_OVER_TWO, Vectors::UNIT_X); // Vertical + static const vec3 DEFAULT_EMIT_DIMENSIONS = Vectors::ZERO; // Emit from point static const float MINIMUM_EMIT_DIMENSION = 0.0f; static const float MAXIMUM_EMIT_DIMENSION = (float)TREE_SCALE; static const float DEFAULT_EMIT_RADIUS_START = 1.0f; // Emit from surface (when emitDimensions > 0) @@ -57,10 +57,10 @@ namespace particle { static const float MAXIMUM_AZIMUTH = SCRIPT_MAXIMUM_PI; static const float DEFAULT_AZIMUTH_START = -PI; // Emit full circumference (when polarFinish > 0) static const float DEFAULT_AZIMUTH_FINISH = PI; // "" - static const glm::vec3 DEFAULT_EMIT_ACCELERATION(0.0f, -9.8f, 0.0f); + static const vec3 DEFAULT_EMIT_ACCELERATION(0.0f, -9.8f, 0.0f); static const float MINIMUM_EMIT_ACCELERATION = -100.0f; // ~ 10g static const float MAXIMUM_EMIT_ACCELERATION = 100.0f; - static const glm::vec3 DEFAULT_ACCELERATION_SPREAD(0.0f, 0.0f, 0.0f); + static const vec3 DEFAULT_ACCELERATION_SPREAD(0.0f, 0.0f, 0.0f); static const float MINIMUM_ACCELERATION_SPREAD = 0.0f; static const float MAXIMUM_ACCELERATION_SPREAD = 100.0f; static const float DEFAULT_PARTICLE_RADIUS = 0.025f; @@ -189,10 +189,10 @@ namespace particle { return *this; } - vec4 getColorStart() const { return vec4(ColorUtils::sRGBToLinearVec3(color.range.start / 255.0f), alpha.range.start); } - vec4 getColorMiddle() const { return vec4(ColorUtils::sRGBToLinearVec3(color.gradient.target / 255.0f), alpha.gradient.target); } - vec4 getColorFinish() const { return vec4(ColorUtils::sRGBToLinearVec3(color.range.finish / 255.0f), alpha.range.finish); } - vec4 getColorSpread() const { return vec4(ColorUtils::sRGBToLinearVec3(color.gradient.spread / 255.0f), alpha.gradient.spread); } + vec4 getColorStart() const { return vec4(ColorUtils::sRGBToLinearVec3(toGlm(color.range.start)), alpha.range.start); } + vec4 getColorMiddle() const { return vec4(ColorUtils::sRGBToLinearVec3(toGlm(color.gradient.target)), alpha.gradient.target); } + vec4 getColorFinish() const { return vec4(ColorUtils::sRGBToLinearVec3(toGlm(color.range.finish)), alpha.range.finish); } + vec4 getColorSpread() const { return vec4(ColorUtils::sRGBToLinearVec3(toGlm(color.gradient.spread)), alpha.gradient.spread); } }; } // namespace particles @@ -228,11 +228,8 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; - xColor getXColor() const; - vec3 getColor() const { return _particleProperties.color.gradient.target; } - - void setColor(const vec3& value); - void setColor(const xColor& value); + void setColor(const glm::u8vec3& value); + glm::u8vec3 getColor() const { return _particleProperties.color.gradient.target; } void setColorStart(const vec3& colorStart); vec3 getColorStart() const { return _particleProperties.color.range.start; } @@ -240,8 +237,8 @@ public: void setColorFinish(const vec3& colorFinish); vec3 getColorFinish() const { return _particleProperties.color.range.finish; } - void setColorSpread(const xColor& colorSpread); - xColor getColorSpread() const; + void setColorSpread(const glm::u8vec3& colorSpread); + glm::u8vec3 getColorSpread() const { return _particleProperties.color.gradient.spread; } void setAlpha(float alpha); float getAlpha() const { return _particleProperties.alpha.gradient.target; } @@ -344,9 +341,6 @@ public: particle::Properties getParticleProperties() const; - static const xColor DEFAULT_XCOLOR; - static const xColor DEFAULT_XCOLOR_SPREAD; - protected: particle::Properties _particleProperties; bool _isEmitting { true }; diff --git a/libraries/entities/src/PolyLineEntityItem.cpp b/libraries/entities/src/PolyLineEntityItem.cpp index 2d0689b56a..c72256822d 100644 --- a/libraries/entities/src/PolyLineEntityItem.cpp +++ b/libraries/entities/src/PolyLineEntityItem.cpp @@ -39,12 +39,8 @@ PolyLineEntityItem::PolyLineEntityItem(const EntityItemID& entityItemID) : Entit EntityItemProperties PolyLineEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { QWriteLocker lock(&_quadReadWriteLock); EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class - - - properties._color = getXColor(); - properties._colorChanged = false; - + COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(lineWidth, getLineWidth); COPY_ENTITY_PROPERTY_TO_PROPERTIES(linePoints, getLinePoints); COPY_ENTITY_PROPERTY_TO_PROPERTIES(normals, getNormals); @@ -204,7 +200,7 @@ int PolyLineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* da int bytesRead = 0; const unsigned char* dataAt = data; - READ_ENTITY_PROPERTY(PROP_COLOR, rgbColor, setColor); + READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor); READ_ENTITY_PROPERTY(PROP_LINE_WIDTH, float, setLineWidth); READ_ENTITY_PROPERTY(PROP_LINE_POINTS, QVector, setLinePoints); READ_ENTITY_PROPERTY(PROP_NORMALS, QVector, setNormals); @@ -253,7 +249,7 @@ void PolyLineEntityItem::appendSubclassData(OctreePacketData* packetData, Encode void PolyLineEntityItem::debugDump() const { quint64 now = usecTimestampNow(); qCDebug(entities) << " QUAD EntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " color:" << _color[0] << "," << _color[1] << "," << _color[2]; + qCDebug(entities) << " color:" << _color; qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); @@ -261,7 +257,7 @@ void PolyLineEntityItem::debugDump() const { -QVector PolyLineEntityItem::getLinePoints() const { +QVector PolyLineEntityItem::getLinePoints() const { QVector result; withReadLock([&] { result = _points; @@ -269,7 +265,7 @@ QVector PolyLineEntityItem::getLinePoints() const { return result; } -QVector PolyLineEntityItem::getNormals() const { +QVector PolyLineEntityItem::getNormals() const { QVector result; withReadLock([&] { result = _normals; @@ -309,3 +305,16 @@ void PolyLineEntityItem::setTextures(const QString& textures) { } }); } + +void PolyLineEntityItem::setColor(const glm::u8vec3& value) { + withWriteLock([&] { + _strokeColorsChanged = true; + _color = value; + }); +} + +glm::u8vec3 PolyLineEntityItem::getColor() const { + return resultWithReadLock([&] { + return _color; + }); +} diff --git a/libraries/entities/src/PolyLineEntityItem.h b/libraries/entities/src/PolyLineEntityItem.h index 94b46ac5e2..f640bd7a9e 100644 --- a/libraries/entities/src/PolyLineEntityItem.h +++ b/libraries/entities/src/PolyLineEntityItem.h @@ -41,19 +41,8 @@ class PolyLineEntityItem : public EntityItem { EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; - const rgbColor& getColor() const { return _color; } - xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; } - - void setColor(const rgbColor& value) { - _strokeColorsChanged = true; - memcpy(_color, value, sizeof(_color)); - } - void setColor(const xColor& value) { - _strokeColorsChanged = true; - _color[RED_INDEX] = value.red; - _color[GREEN_INDEX] = value.green; - _color[BLUE_INDEX] = value.blue; - } + glm::u8vec3 getColor() const; + void setColor(const glm::u8vec3& value); void setLineWidth(float lineWidth){ _lineWidth = lineWidth; } float getLineWidth() const{ return _lineWidth; } @@ -109,7 +98,7 @@ private: void calculateScaleAndRegistrationPoint(); protected: - rgbColor _color; + glm::u8vec3 _color; float _lineWidth { DEFAULT_LINE_WIDTH }; bool _pointsChanged { true }; bool _normalsChanged { true }; diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index 484a6110c3..e65742b36c 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -70,7 +70,7 @@ PolyVoxEntityItem::PolyVoxEntityItem(const EntityItemID& entityItemID) : EntityI _type = EntityTypes::PolyVox; } -void PolyVoxEntityItem::setVoxelVolumeSize(const vec3& voxelVolumeSize) { +void PolyVoxEntityItem::setVoxelVolumeSize(const glm::vec3& voxelVolumeSize) { withWriteLock([&] { assert(!glm::any(glm::isnan(voxelVolumeSize))); diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index ba238ab24f..dd05ce67b1 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -54,7 +54,7 @@ class PolyVoxEntityItem : public EntityItem { virtual void debugDump() const override; - virtual void setVoxelVolumeSize(const vec3& voxelVolumeSize); + virtual void setVoxelVolumeSize(const glm::vec3& voxelVolumeSize); virtual glm::vec3 getVoxelVolumeSize() const; virtual void setVoxelData(const QByteArray& voxelData); diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index a5e4e25e67..f67134da0a 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -73,7 +73,7 @@ namespace entity { return Shape::Sphere; } - ::QString stringFromShape(Shape shape) { + QString stringFromShape(Shape shape) { return shapeStrings[shape]; } } @@ -117,8 +117,10 @@ ShapeEntityItem::ShapeEntityItem(const EntityItemID& entityItemID) : EntityItem( EntityItemProperties ShapeEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class + properties.setShape(entity::stringFromShape(getShape())); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor); + properties._shapeChanged = false; + COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha); return properties; @@ -182,7 +184,7 @@ int ShapeEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, const unsigned char* dataAt = data; READ_ENTITY_PROPERTY(PROP_SHAPE, QString, setShape); - READ_ENTITY_PROPERTY(PROP_COLOR, rgbColor, setColor); + READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor); READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha); return bytesRead; @@ -210,32 +212,24 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha()); } -void ShapeEntityItem::setColor(const rgbColor& value) { - memcpy(_color, value, sizeof(rgbColor)); - _material->setAlbedo(glm::vec3(_color[0], _color[1], _color[2]) / 255.0f); +void ShapeEntityItem::setColor(const glm::u8vec3& value) { + withWriteLock([&] { + _color = value; + _material->setAlbedo(toGlm(_color)); + }); } -xColor ShapeEntityItem::getXColor() const { - return xColor { _color[0], _color[1], _color[2] }; -} - -void ShapeEntityItem::setColor(const xColor& value) { - setColor(rgbColor { value.red, value.green, value.blue }); -} - -QColor ShapeEntityItem::getQColor() const { - auto& color = getColor(); - return QColor(color[0], color[1], color[2], (int)(getAlpha() * 255)); -} - -void ShapeEntityItem::setColor(const QColor& value) { - setColor(rgbColor { (uint8_t)value.red(), (uint8_t)value.green(), (uint8_t)value.blue() }); - setAlpha(value.alpha()); +glm::u8vec3 ShapeEntityItem::getColor() const { + return resultWithReadLock([&] { + return _color; + }); } void ShapeEntityItem::setAlpha(float alpha) { - _alpha = alpha; - _material->setOpacity(alpha); + withWriteLock([&] { + _alpha = alpha; + _material->setOpacity(alpha); + }); } void ShapeEntityItem::setUnscaledDimensions(const glm::vec3& value) { @@ -313,7 +307,7 @@ void ShapeEntityItem::debugDump() const { qCDebug(entities) << " name:" << _name; qCDebug(entities) << " shape:" << stringFromShape(_shape) << " (EnumId: " << _shape << " )"; qCDebug(entities) << " collisionShapeType:" << ShapeInfo::getNameForShapeType(getShapeType()); - qCDebug(entities) << " color:" << _color[0] << "," << _color[1] << "," << _color[2]; + qCDebug(entities) << " color:" << _color; qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); @@ -419,5 +413,4 @@ void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) { // This value specifies how the shape should be treated by physics calculations. ShapeType ShapeEntityItem::getShapeType() const { return _collisionShapeType; -} - +} \ No newline at end of file diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h index d0326a3794..c89a8934f8 100644 --- a/libraries/entities/src/ShapeEntityItem.h +++ b/libraries/entities/src/ShapeEntityItem.h @@ -31,7 +31,7 @@ namespace entity { }; Shape shapeFromString(const ::QString& shapeString); - ::QString stringFromShape(Shape shape); + QString stringFromShape(Shape shape); } class ShapeEntityItem : public EntityItem { @@ -77,17 +77,11 @@ public: float getAlpha() const { return _alpha; }; void setAlpha(float alpha); - const rgbColor& getColor() const { return _color; } - void setColor(const rgbColor& value); + glm::u8vec3 getColor() const; + void setColor(const glm::u8vec3& value); void setUnscaledDimensions(const glm::vec3& value) override; - xColor getXColor() const; - void setColor(const xColor& value); - - QColor getQColor() const; - void setColor(const QColor& value); - bool shouldBePhysical() const override { return !isDead(); } bool supportsDetailedIntersection() const override; @@ -110,7 +104,7 @@ public: protected: float _alpha { 1.0f }; - rgbColor _color; + glm::u8vec3 _color; entity::Shape _shape { entity::Shape::Sphere }; //! This is SHAPE_TYPE_ELLIPSOID rather than SHAPE_TYPE_NONE to maintain diff --git a/libraries/entities/src/SkyboxPropertyGroup.cpp b/libraries/entities/src/SkyboxPropertyGroup.cpp index ba40c3fa6f..89ffa95dbe 100644 --- a/libraries/entities/src/SkyboxPropertyGroup.cpp +++ b/libraries/entities/src/SkyboxPropertyGroup.cpp @@ -16,15 +16,15 @@ #include "EntityItemProperties.h" #include "EntityItemPropertiesMacros.h" -const xColor SkyboxPropertyGroup::DEFAULT_COLOR = { 0, 0, 0 }; +const glm::u8vec3 SkyboxPropertyGroup::DEFAULT_COLOR = { 0, 0, 0 }; void SkyboxPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_SKYBOX_COLOR, Skybox, skybox, Color, color); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_SKYBOX_COLOR, Skybox, skybox, Color, color, u8vec3Color); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_SKYBOX_URL, Skybox, skybox, URL, url); } void SkyboxPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(skybox, color, xColor, setColor); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(skybox, color, u8vec3Color, setColor); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(skybox, url, QString, setURL); } @@ -71,7 +71,7 @@ bool SkyboxPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlag bool overwriteLocalData = true; bool somethingChanged = false; - READ_ENTITY_PROPERTY(PROP_SKYBOX_COLOR, xColor, setColor); + READ_ENTITY_PROPERTY(PROP_SKYBOX_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY(PROP_SKYBOX_URL, QString, setURL); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_SKYBOX_COLOR, Color); @@ -143,7 +143,7 @@ int SkyboxPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* d int bytesRead = 0; const unsigned char* dataAt = data; - READ_ENTITY_PROPERTY(PROP_SKYBOX_COLOR, xColor, setColor); + READ_ENTITY_PROPERTY(PROP_SKYBOX_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY(PROP_SKYBOX_URL, QString, setURL); return bytesRead; diff --git a/libraries/entities/src/SkyboxPropertyGroup.h b/libraries/entities/src/SkyboxPropertyGroup.h index a94365d24d..c3f9b421f4 100644 --- a/libraries/entities/src/SkyboxPropertyGroup.h +++ b/libraries/entities/src/SkyboxPropertyGroup.h @@ -32,7 +32,7 @@ class ReadBitstreamToTreeParams; /**jsdoc * A skybox is defined by the following properties. * @typedef {object} Entities.Skybox - * @property {Color} color=0,0,0 - Sets the color of the sky if url is "", otherwise modifies the + * @property {Color} color=0,0,0 - Sets the color of the sky if url is "", otherwise modifies the * color of the cube map image. * @property {string} url="" - A cube map image that is used to render the sky. */ @@ -83,16 +83,8 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; - glm::vec3 getColorVec3() const { - const quint8 MAX_COLOR = 255; - glm::vec3 color = { (float)_color.red / (float)MAX_COLOR, - (float)_color.green / (float)MAX_COLOR, - (float)_color.blue / (float)MAX_COLOR }; - return color; - } - - static const xColor DEFAULT_COLOR; - DEFINE_PROPERTY_REF(PROP_SKYBOX_COLOR, Color, color, xColor, DEFAULT_COLOR); + static const glm::u8vec3 DEFAULT_COLOR; + DEFINE_PROPERTY_REF(PROP_SKYBOX_COLOR, Color, color, glm::u8vec3, DEFAULT_COLOR); DEFINE_PROPERTY_REF(PROP_SKYBOX_URL, URL, url, QString, ""); }; diff --git a/libraries/entities/src/TextEntityItem.cpp b/libraries/entities/src/TextEntityItem.cpp index da843256a0..8dd4877ce2 100644 --- a/libraries/entities/src/TextEntityItem.cpp +++ b/libraries/entities/src/TextEntityItem.cpp @@ -25,8 +25,8 @@ const QString TextEntityItem::DEFAULT_TEXT(""); const float TextEntityItem::DEFAULT_LINE_HEIGHT = 0.1f; -const xColor TextEntityItem::DEFAULT_TEXT_COLOR = { 255, 255, 255 }; -const xColor TextEntityItem::DEFAULT_BACKGROUND_COLOR = { 0, 0, 0}; +const glm::u8vec3 TextEntityItem::DEFAULT_TEXT_COLOR = { 255, 255, 255 }; +const glm::u8vec3 TextEntityItem::DEFAULT_BACKGROUND_COLOR = { 0, 0, 0}; const bool TextEntityItem::DEFAULT_FACE_CAMERA = false; EntityItemPointer TextEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { @@ -51,8 +51,8 @@ EntityItemProperties TextEntityItem::getProperties(const EntityPropertyFlags& de COPY_ENTITY_PROPERTY_TO_PROPERTIES(text, getText); COPY_ENTITY_PROPERTY_TO_PROPERTIES(lineHeight, getLineHeight); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(textColor, getTextColorX); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(backgroundColor, getBackgroundColorX); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(textColor, getTextColor); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(backgroundColor, getBackgroundColor); COPY_ENTITY_PROPERTY_TO_PROPERTIES(faceCamera, getFaceCamera); return properties; } @@ -91,8 +91,8 @@ int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY(PROP_TEXT, QString, setText); READ_ENTITY_PROPERTY(PROP_LINE_HEIGHT, float, setLineHeight); - READ_ENTITY_PROPERTY(PROP_TEXT_COLOR, rgbColor, setTextColor); - READ_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, rgbColor, setBackgroundColor); + READ_ENTITY_PROPERTY(PROP_TEXT_COLOR, glm::u8vec3, setTextColor); + READ_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, glm::u8vec3, setBackgroundColor); READ_ENTITY_PROPERTY(PROP_FACE_CAMERA, bool, setFaceCamera); return bytesRead; @@ -206,55 +206,27 @@ float TextEntityItem::getLineHeight() const { return result; } -const rgbColor& TextEntityItem::getTextColor() const { - return _textColor; -} - -const rgbColor& TextEntityItem::getBackgroundColor() const { - return _backgroundColor; -} - -xColor TextEntityItem::getTextColorX() const { - xColor result; - withReadLock([&] { - result = { _textColor[RED_INDEX], _textColor[GREEN_INDEX], _textColor[BLUE_INDEX] }; - }); - return result; -} - -void TextEntityItem::setTextColor(const rgbColor& value) { +void TextEntityItem::setTextColor(const glm::u8vec3& value) { withWriteLock([&] { - memcpy(_textColor, value, sizeof(_textColor)); + _textColor = value; }); } -void TextEntityItem::setTextColor(const xColor& value) { +glm::u8vec3 TextEntityItem::getTextColor() const { + return resultWithReadLock([&] { + return _textColor; + }); +} + +void TextEntityItem::setBackgroundColor(const glm::u8vec3& value) { withWriteLock([&] { - _textColor[RED_INDEX] = value.red; - _textColor[GREEN_INDEX] = value.green; - _textColor[BLUE_INDEX] = value.blue; + _backgroundColor = value; }); } -xColor TextEntityItem::getBackgroundColorX() const { - xColor result; - withReadLock([&] { - result = { _backgroundColor[RED_INDEX], _backgroundColor[GREEN_INDEX], _backgroundColor[BLUE_INDEX] }; - }); - return result; -} - -void TextEntityItem::setBackgroundColor(const rgbColor& value) { - withWriteLock([&] { - memcpy(_backgroundColor, value, sizeof(_backgroundColor)); - }); -} - -void TextEntityItem::setBackgroundColor(const xColor& value) { - withWriteLock([&] { - _backgroundColor[RED_INDEX] = value.red; - _backgroundColor[GREEN_INDEX] = value.green; - _backgroundColor[BLUE_INDEX] = value.blue; +glm::u8vec3 TextEntityItem::getBackgroundColor() const { + return resultWithReadLock([&] { + return _backgroundColor; }); } diff --git a/libraries/entities/src/TextEntityItem.h b/libraries/entities/src/TextEntityItem.h index cbddf87fda..357697fdec 100644 --- a/libraries/entities/src/TextEntityItem.h +++ b/libraries/entities/src/TextEntityItem.h @@ -63,21 +63,13 @@ public: void setLineHeight(float value); float getLineHeight() const; - static const xColor DEFAULT_TEXT_COLOR; - // FIXME should not return a reference because of thread safety, but can't return an array - const rgbColor& getTextColor() const; - xColor getTextColorX() const; + static const glm::u8vec3 DEFAULT_TEXT_COLOR; + glm::u8vec3 getTextColor() const; + void setTextColor(const glm::u8vec3& value); - void setTextColor(const rgbColor& value); - void setTextColor(const xColor& value); - - static const xColor DEFAULT_BACKGROUND_COLOR; - // FIXME should not return a reference because of thread safety, but can't return an array - const rgbColor& getBackgroundColor() const; - xColor getBackgroundColorX() const; - - void setBackgroundColor(const rgbColor& value); - void setBackgroundColor(const xColor& value); + static const glm::u8vec3 DEFAULT_BACKGROUND_COLOR; + glm::u8vec3 getBackgroundColor() const; + void setBackgroundColor(const glm::u8vec3& value); static const bool DEFAULT_FACE_CAMERA; bool getFaceCamera() const; @@ -86,8 +78,8 @@ public: private: QString _text; float _lineHeight; - rgbColor _textColor; - rgbColor _backgroundColor; + glm::u8vec3 _textColor; + glm::u8vec3 _backgroundColor; bool _faceCamera; }; diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp index 13c21d89e7..1203e65685 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp @@ -67,6 +67,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::gl::GLBackend::do_clearFramebuffer), (&::gpu::gl::GLBackend::do_blit), (&::gpu::gl::GLBackend::do_generateTextureMips), + (&::gpu::gl::GLBackend::do_generateTextureMipsWithPipeline), (&::gpu::gl::GLBackend::do_advance), @@ -166,6 +167,10 @@ GLBackend::GLBackend() { GLBackend::~GLBackend() {} void GLBackend::shutdown() { + if (_mipGenerationFramebufferId) { + glDeleteFramebuffers(1, &_mipGenerationFramebufferId); + _mipGenerationFramebufferId = 0; + } killInput(); killTransform(); killTextureManagementStage(); diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h index 0b76ef17de..267c2a97ad 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h @@ -288,6 +288,7 @@ public: virtual void do_setIndexBuffer(const Batch& batch, size_t paramOffset) final; virtual void do_setIndirectBuffer(const Batch& batch, size_t paramOffset) final; virtual void do_generateTextureMips(const Batch& batch, size_t paramOffset) final; + virtual void do_generateTextureMipsWithPipeline(const Batch& batch, size_t paramOffset) final; // Transform Stage virtual void do_setModelTransform(const Batch& batch, size_t paramOffset) final; @@ -407,6 +408,8 @@ public: protected: virtual GLint getRealUniformLocation(GLint location) const; + virtual void draw(GLenum mode, uint32 numVertices, uint32 startVertex) = 0; + void recycle() const override; // FIXME instead of a single flag, create a features struct similar to @@ -696,6 +699,8 @@ protected: virtual void initTextureManagementStage(); virtual void killTextureManagementStage(); + GLuint _mipGenerationFramebufferId{ 0 }; + typedef void (GLBackend::*CommandCall)(const Batch&, size_t); static CommandCall _commandCalls[Batch::NUM_COMMANDS]; friend class GLState; diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp index f4fb3fcf2c..b74ff079d7 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendTexture.cpp @@ -79,3 +79,55 @@ void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) { object->generateMips(); } + +void GLBackend::do_generateTextureMipsWithPipeline(const Batch& batch, size_t paramOffset) { + TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); + if (!resourceTexture) { + return; + } + + // Always make sure the GLObject is in sync + GLTexture* object = syncGPUObject(resourceTexture); + if (object) { + GLuint to = object->_texture; + glActiveTexture(GL_TEXTURE0 + gpu::slot::texture::MipCreationInput); + glBindTexture(object->_target, to); + (void)CHECK_GL_ERROR(); + } else { + return; + } + + auto numMips = batch._params[paramOffset + 1]._int; + if (numMips < 0) { + numMips = resourceTexture->getNumMips(); + } else { + numMips = std::min(numMips, (int)resourceTexture->getNumMips()); + } + + if (_mipGenerationFramebufferId == 0) { + glGenFramebuffers(1, &_mipGenerationFramebufferId); + Q_ASSERT(_mipGenerationFramebufferId > 0); + } + + glBindFramebuffer(GL_FRAMEBUFFER, _mipGenerationFramebufferId); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); + + for (int level = 1; level < numMips; level++) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, object->_id, level); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, level - 1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level - 1); + + const auto mipDimensions = resourceTexture->evalMipDimensions(level); + glViewport(0, 0, mipDimensions.x, mipDimensions.y); + draw(GL_TRIANGLE_STRIP, 4, 0); + } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, numMips - 1); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + resetOutputStage(); + // Restore viewport + ivec4& vp = _transform._viewport; + glViewport(vp.x, vp.y, vp.z, vp.w); +} diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp index 88cf89ad99..43ae4691b9 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp @@ -20,6 +20,29 @@ using namespace gpu::gl41; const std::string GL41Backend::GL41_VERSION { "GL41" }; +void GL41Backend::draw(GLenum mode, uint32 numVertices, uint32 startVertex) { + if (isStereo()) { +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glDrawArraysInstanced(mode, startVertex, numVertices, 2); +#else + setupStereoSide(0); + glDrawArrays(mode, startVertex, numVertices); + setupStereoSide(1); + glDrawArrays(mode, startVertex, numVertices); +#endif + _stats._DSNumTriangles += 2 * numVertices / 3; + _stats._DSNumDrawcalls += 2; + + } else { + glDrawArrays(mode, startVertex, numVertices); + _stats._DSNumTriangles += numVertices / 3; + _stats._DSNumDrawcalls++; + } + _stats._DSNumAPIDrawcalls++; + + (void)CHECK_GL_ERROR(); +} + void GL41Backend::do_draw(const Batch& batch, size_t paramOffset) { Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index e5f7415107..5d691d032a 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -130,6 +130,9 @@ public: }; protected: + + void draw(GLenum mode, uint32 numVertices, uint32 startVertex) override; + GLuint getFramebufferID(const FramebufferPointer& framebuffer) override; GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) override; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp index bbe011d237..e86eae2c2d 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.cpp @@ -42,6 +42,30 @@ void GL45Backend::recycle() const { Parent::recycle(); } +void GL45Backend::draw(GLenum mode, uint32 numVertices, uint32 startVertex) { + if (isStereo()) { +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glDrawArraysInstanced(mode, startVertex, numVertices, 2); +#else + setupStereoSide(0); + glDrawArrays(mode, startVertex, numVertices); + setupStereoSide(1); + glDrawArrays(mode, startVertex, numVertices); +#endif + + _stats._DSNumTriangles += 2 * numVertices / 3; + _stats._DSNumDrawcalls += 2; + + } else { + glDrawArrays(mode, startVertex, numVertices); + _stats._DSNumTriangles += numVertices / 3; + _stats._DSNumDrawcalls++; + } + _stats._DSNumAPIDrawcalls++; + + (void)CHECK_GL_ERROR(); +} + void GL45Backend::do_draw(const Batch& batch, size_t paramOffset) { Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 30656b47c7..77095375af 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -229,6 +229,7 @@ public: protected: + void draw(GLenum mode, uint32 numVertices, uint32 startVertex) override; void recycle() const override; GLuint getFramebufferID(const FramebufferPointer& framebuffer) override; diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackend.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackend.cpp index b277889771..cb40b4fc9b 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackend.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackend.cpp @@ -20,6 +20,29 @@ using namespace gpu::gles; const std::string GLESBackend::GLES_VERSION { "GLES" }; +void GLESBackend::draw(GLenum mode, uint32 numVertices, uint32 startVertex) { + if (isStereo()) { +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glDrawArraysInstanced(mode, startVertex, numVertices, 2); +#else + setupStereoSide(0); + glDrawArrays(mode, startVertex, numVertices); + setupStereoSide(1); + glDrawArrays(mode, startVertex, numVertices); +#endif + _stats._DSNumTriangles += 2 * numVertices / 3; + _stats._DSNumDrawcalls += 2; + + } else { + glDrawArrays(mode, startVertex, numVertices); + _stats._DSNumTriangles += numVertices / 3; + _stats._DSNumDrawcalls++; + } + _stats._DSNumAPIDrawcalls++; + + (void)CHECK_GL_ERROR(); +} + void GLESBackend::do_draw(const Batch& batch, size_t paramOffset) { Primitive primitiveType = (Primitive)batch._params[paramOffset + 2]._uint; GLenum mode = gl::PRIMITIVE_TO_GL[primitiveType]; diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackend.h b/libraries/gpu-gles/src/gpu/gles/GLESBackend.h index 56ae41da31..7f6765c129 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackend.h +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackend.h @@ -126,6 +126,9 @@ public: }; protected: + + void draw(GLenum mode, uint32 numVertices, uint32 startVertex) override; + GLuint getFramebufferID(const FramebufferPointer& framebuffer) override; GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) override; diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index 018ca8f02f..e3ea210ecb 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -426,6 +426,13 @@ void Batch::generateTextureMips(const TexturePointer& texture) { _params.emplace_back(_textures.cache(texture)); } +void Batch::generateTextureMipsWithPipeline(const TexturePointer& texture, int numMips) { + ADD_COMMAND(generateTextureMipsWithPipeline); + + _params.emplace_back(_textures.cache(texture)); + _params.emplace_back(numMips); +} + void Batch::beginQuery(const QueryPointer& query) { ADD_COMMAND(beginQuery); diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 0f9c2f554b..96a45d3111 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -226,6 +226,8 @@ public: // Generate the mips for a texture void generateTextureMips(const TexturePointer& texture); + // Generate the mips for a texture using the current pipeline + void generateTextureMipsWithPipeline(const TexturePointer& destTexture, int numMips = -1); // Query Section void beginQuery(const QueryPointer& query); @@ -326,6 +328,7 @@ public: COMMAND_clearFramebuffer, COMMAND_blit, COMMAND_generateTextureMips, + COMMAND_generateTextureMipsWithPipeline, COMMAND_advance, diff --git a/libraries/gpu/src/gpu/MipGeneration.slh b/libraries/gpu/src/gpu/MipGeneration.slh new file mode 100644 index 0000000000..bc8dd39042 --- /dev/null +++ b/libraries/gpu/src/gpu/MipGeneration.slh @@ -0,0 +1,20 @@ + +<@if not MIP_GENERATION_SLH@> +<@def MIP_GENERATION_SLH@> + +<@include gpu/ShaderConstants.h@> + +layout(binding=GPU_TEXTURE_MIP_CREATION_INPUT) uniform sampler2D texMap; + +in vec2 varTexCoord0; + +<@endif@> \ No newline at end of file diff --git a/libraries/gpu/src/gpu/ShaderConstants.h b/libraries/gpu/src/gpu/ShaderConstants.h index 13dfd1be9c..0724b4eb40 100644 --- a/libraries/gpu/src/gpu/ShaderConstants.h +++ b/libraries/gpu/src/gpu/ShaderConstants.h @@ -21,6 +21,9 @@ #define GPU_TEXTURE_TRANSFORM_OBJECT 31 +// Mip creation +#define GPU_TEXTURE_MIP_CREATION_INPUT 30 + #define GPU_STORAGE_TRANSFORM_OBJECT 7 #define GPU_ATTR_POSITION 0 @@ -67,7 +70,8 @@ enum Buffer { namespace texture { enum Texture { ObjectTransforms = GPU_TEXTURE_TRANSFORM_OBJECT, -}; + MipCreationInput = GPU_TEXTURE_MIP_CREATION_INPUT, +}; } // namespace texture namespace storage { diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index f2afc1e7d4..67f79db6b7 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -367,8 +367,8 @@ namespace scriptable { obj.setProperty("metallic", material.metallic); obj.setProperty("scattering", material.scattering); obj.setProperty("unlit", material.unlit); - obj.setProperty("emissive", vec3toScriptValue(engine, material.emissive)); - obj.setProperty("albedo", vec3toScriptValue(engine, material.albedo)); + obj.setProperty("emissive", vec3ColorToScriptValue(engine, material.emissive)); + obj.setProperty("albedo", vec3ColorToScriptValue(engine, material.albedo)); obj.setProperty("emissiveMap", material.emissiveMap); obj.setProperty("albedoMap", material.albedoMap); obj.setProperty("opacityMap", material.opacityMap); diff --git a/libraries/model-networking/src/model-networking/MaterialCache.cpp b/libraries/model-networking/src/model-networking/MaterialCache.cpp index 823602d939..e6e3b0e812 100644 --- a/libraries/model-networking/src/model-networking/MaterialCache.cpp +++ b/libraries/model-networking/src/model-networking/MaterialCache.cpp @@ -113,11 +113,11 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater * @typedef {object} Material * @property {string} name="" - A name for the material. * @property {string} model="hifi_pbr" - Currently not used. - * @property {Vec3Color|RGBS} emissive - The emissive color, i.e., the color that the material emits. A {@link Vec3Color} value + * @property {Color|RGBS} emissive - The emissive color, i.e., the color that the material emits. A {@link Color} value * is treated as sRGB. A {@link RGBS} value can be either RGB or sRGB. * @property {number} opacity=1.0 - The opacity, 0.01.0. * @property {boolean} unlit=false - If true, the material is not lit. - * @property {Vec3Color|RGBS} albedo - The albedo color. A {@link Vec3Color} value is treated as sRGB. A {@link RGBS} value can + * @property {Color|RGBS} albedo - The albedo color. A {@link Color} value is treated as sRGB. A {@link RGBS} value can * be either RGB or sRGB. * @property {number} roughness - The roughness, 0.01.0. * @property {number} metallic - The metallicness, 0.01.0. diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 615546b410..182a79ec4b 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -140,8 +140,10 @@ void DomainHandler::hardReset() { } bool DomainHandler::isHardRefusal(int reasonCode) { - return (reasonCode == (int)ConnectionRefusedReason::ProtocolMismatch || reasonCode == (int)ConnectionRefusedReason::NotAuthorized || - reasonCode == (int)ConnectionRefusedReason::TimedOut); + return (reasonCode == (int)ConnectionRefusedReason::ProtocolMismatch || + reasonCode == (int)ConnectionRefusedReason::TooManyUsers || + reasonCode == (int)ConnectionRefusedReason::NotAuthorized || + reasonCode == (int)ConnectionRefusedReason::TimedOut); } bool DomainHandler::getInterstitialModeEnabled() const { @@ -473,7 +475,7 @@ bool DomainHandler::reasonSuggestsLogin(ConnectionRefusedReason reasonCode) { case ConnectionRefusedReason::LoginError: case ConnectionRefusedReason::NotAuthorized: return true; - + default: case ConnectionRefusedReason::Unknown: case ConnectionRefusedReason::ProtocolMismatch: diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 8cb7c9aaa4..7cd2330753 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -33,14 +33,14 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return static_cast(EntityVersion::BloomEffect); + return static_cast(EntityVersion::ScriptGlmVectors); case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::ConicalFrustums); case PacketType::AvatarIdentity: case PacketType::AvatarData: case PacketType::BulkAvatarData: case PacketType::KillAvatar: - return static_cast(AvatarMixerPacketVersion::MigrateAvatarEntitiesToTraits); + return static_cast(AvatarMixerPacketVersion::FarGrabJointsRedux); case PacketType::MessagesData: return static_cast(MessageDataVersion::TextOrBinaryData); // ICE packets diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 84a93ac939..c40ee69c08 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -242,7 +242,9 @@ enum class EntityVersion : PacketVersion { YieldSimulationOwnership, ParticleEntityFix, ParticleSpin, - BloomEffect + BloomEffect, + GrabProperties, + ScriptGlmVectors }; enum class EntityScriptCallMethodVersion : PacketVersion { @@ -293,7 +295,8 @@ enum class AvatarMixerPacketVersion : PacketVersion { ProceduralFaceMovementFlagsAndBlendshapes, FarGrabJoints, MigrateSkeletonURLToTraits, - MigrateAvatarEntitiesToTraits + MigrateAvatarEntitiesToTraits, + FarGrabJointsRedux }; enum class DomainConnectRequestVersion : PacketVersion { diff --git a/libraries/octree/src/OctreePacketData.cpp b/libraries/octree/src/OctreePacketData.cpp index b938850684..88e83c01c8 100644 --- a/libraries/octree/src/OctreePacketData.cpp +++ b/libraries/octree/src/OctreePacketData.cpp @@ -198,17 +198,17 @@ const unsigned char* OctreePacketData::getFinalizedData() { int OctreePacketData::getFinalizedSize() { if (!_enableCompression) { - return _bytesInUse; + return _bytesInUse; } if (_dirty) { if (_debug) { qCDebug(octree, "getFinalizedSize() _compressedBytes=%d _bytesInUse=%d",_compressedBytes, _bytesInUse); } - compressContent(); + compressContent(); } - return _compressedBytes; + return _compressedBytes; } @@ -297,14 +297,6 @@ bool OctreePacketData::appendValue(const nodeColor& color) { return appendColor(color[RED_INDEX], color[GREEN_INDEX], color[BLUE_INDEX]); } -bool OctreePacketData::appendValue(const xColor& color) { - return appendColor(color.red, color.green, color.blue); -} - -bool OctreePacketData::appendValue(const rgbColor& color) { - return appendColor(color[RED_INDEX], color[GREEN_INDEX], color[BLUE_INDEX]); -} - bool OctreePacketData::appendColor(colorPart red, colorPart green, colorPart blue) { // eventually we can make this use a dictionary... bool success = false; @@ -371,7 +363,6 @@ bool OctreePacketData::appendValue(quint64 value) { } bool OctreePacketData::appendValue(float value) { - const unsigned char* data = (const unsigned char*)&value; int length = sizeof(value); bool success = append(data, length); @@ -384,7 +375,7 @@ bool OctreePacketData::appendValue(float value) { bool OctreePacketData::appendValue(const glm::vec2& value) { const unsigned char* data = (const unsigned char*)&value; - int length = sizeof(value); + int length = sizeof(glm::vec2); bool success = append(data, length); if (success) { _bytesOfValues += length; @@ -395,7 +386,7 @@ bool OctreePacketData::appendValue(const glm::vec2& value) { bool OctreePacketData::appendValue(const glm::vec3& value) { const unsigned char* data = (const unsigned char*)&value; - int length = sizeof(value); + int length = sizeof(glm::vec3); bool success = append(data, length); if (success) { _bytesOfValues += length; @@ -404,6 +395,10 @@ bool OctreePacketData::appendValue(const glm::vec3& value) { return success; } +bool OctreePacketData::appendValue(const glm::u8vec3& color) { + return appendColor(color.x, color.y, color.z); +} + bool OctreePacketData::appendValue(const QVector& value) { uint16_t qVecSize = value.size(); bool success = appendValue(qVecSize); @@ -604,6 +599,9 @@ bool OctreePacketData::compressContent() { memcpy(_compressed, compressedData.constData(), _compressedBytes); _dirty = false; success = true; + } else { + qCWarning(octree) << "OctreePacketData::compressContent -- compressedData.size >= MAX_OCTREE_PACKET_DATA_SIZE"; + assert(false); } return success; } @@ -623,11 +621,16 @@ void OctreePacketData::loadFinalizedContent(const unsigned char* data, int lengt memcpy(compressedData.data(), data, _compressedBytes); QByteArray uncompressedData = qUncompress(compressedData); - if (uncompressedData.size() <= _bytesAvailable) { - _bytesInUse = uncompressedData.size(); - _bytesAvailable -= uncompressedData.size(); - memcpy(_uncompressed, uncompressedData.constData(), _bytesInUse); + if (uncompressedData.size() > _bytesAvailable) { + int moreNeeded = uncompressedData.size() - _bytesAvailable; + _uncompressedByteArray.resize(_uncompressedByteArray.size() + moreNeeded); + _uncompressed = (unsigned char*)_uncompressedByteArray.data(); + _bytesAvailable += moreNeeded; } + + _bytesInUse = uncompressedData.size(); + _bytesAvailable -= uncompressedData.size(); + memcpy(_uncompressed, uncompressedData.constData(), _bytesInUse); } else { memcpy(_uncompressed, data, length); memcpy(_compressed, data, length); @@ -673,6 +676,21 @@ void OctreePacketData::debugBytes() { qCDebug(octree) << " _bytesReserved=" << _bytesReserved; } +int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, glm::vec2& result) { + memcpy(&result, dataBytes, sizeof(result)); + return sizeof(result); +} + +int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, glm::vec3& result) { + memcpy(&result, dataBytes, sizeof(result)); + return sizeof(result); +} + +int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, glm::u8vec3& result) { + memcpy(&result, dataBytes, sizeof(result)); + return sizeof(result); +} + int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QString& result) { uint16_t length; memcpy(&length, dataBytes, sizeof(length)); @@ -695,14 +713,6 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QUuid& return sizeof(length) + length; } -int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, xColor& result) { - result.red = dataBytes[RED_INDEX]; - result.green = dataBytes[GREEN_INDEX]; - result.blue = dataBytes[BLUE_INDEX]; - return sizeof(rgbColor); -} - - int OctreePacketData::unpackDataFromBytes(const unsigned char *dataBytes, QVector& result) { uint16_t length; memcpy(&length, dataBytes, sizeof(uint16_t)); diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index 8d8f599fea..46726d83a6 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -143,12 +143,6 @@ public: /// appends a color to the end of the stream, may fail if new data stream is too long to fit in packet bool appendValue(const nodeColor& color); - /// appends a color to the end of the stream, may fail if new data stream is too long to fit in packet - bool appendValue(const xColor& color); - - /// appends a color to the end of the stream, may fail if new data stream is too long to fit in packet - bool appendValue(const rgbColor& color); - /// appends a unsigned 8 bit int to the end of the stream, may fail if new data stream is too long to fit in packet bool appendValue(uint8_t value); @@ -170,6 +164,9 @@ public: /// appends a non-position vector to the end of the stream, may fail if new data stream is too long to fit in packet bool appendValue(const glm::vec3& value); + /// appends a color to the end of the stream, may fail if new data stream is too long to fit in packet + bool appendValue(const glm::u8vec3& value); + /// appends a QVector of vec3s to the end of the stream, may fail if new data stream is too long to fit in packet bool appendValue(const QVector& value); @@ -253,20 +250,19 @@ public: static quint64 getTotalBytesOfColor() { return _totalBytesOfColor; } /// total bytes of color static int unpackDataFromBytes(const unsigned char* dataBytes, float& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } - static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec2& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } - static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec3& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, bool& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, quint64& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, uint32_t& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, uint16_t& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, uint8_t& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } - static int unpackDataFromBytes(const unsigned char* dataBytes, rgbColor& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, glm::quat& result) { int bytes = unpackOrientationQuatFromBytes(dataBytes, result); return bytes; } static int unpackDataFromBytes(const unsigned char* dataBytes, ShapeType& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, MaterialMappingMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } + static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec2& result); + static int unpackDataFromBytes(const unsigned char* dataBytes, glm::vec3& result); + static int unpackDataFromBytes(const unsigned char* dataBytes, glm::u8vec3& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QString& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QUuid& result); - static int unpackDataFromBytes(const unsigned char* dataBytes, xColor& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QVector& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QVector& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QVector& result); diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 8162bf4e18..c920665279 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -502,36 +502,18 @@ void EntityMotionState::sendBid(OctreeEditPacketSender* packetSender, uint32_t s properties.setVelocity(linearVelocity); properties.setAcceleration(_entity->getAcceleration()); properties.setAngularVelocity(angularVelocity); - if (_entity->dynamicDataNeedsTransmit()) { - _entity->setDynamicDataNeedsTransmit(false); - properties.setActionData(_entity->getDynamicData()); - } - - if (_entity->updateQueryAACube()) { - // due to parenting, the server may not know where something is in world-space, so include the bounding cube. - properties.setQueryAACube(_entity->getQueryAACube()); - } - - // set the LastEdited of the properties but NOT the entity itself - quint64 now = usecTimestampNow(); - properties.setLastEdited(now); // we don't own the simulation for this entity yet, but we're sending a bid for it + quint64 now = usecTimestampNow(); uint8_t finalBidPriority = computeFinalBidPriority(); - _entity->clearScriptSimulationPriority(); - properties.setSimulationOwner(Physics::getSessionUUID(), finalBidPriority); - _entity->setPendingOwnershipPriority(finalBidPriority); + _entity->prepareForSimulationOwnershipBid(properties, now, finalBidPriority); EntityTreeElementPointer element = _entity->getElement(); EntityTreePointer tree = element ? element->getTree() : nullptr; - properties.setClientOnly(_entity->getClientOnly()); - properties.setOwningAvatarID(_entity->getOwningAvatarID()); - EntityItemID id(_entity->getID()); EntityEditPacketSender* entityPacketSender = static_cast(packetSender); entityPacketSender->queueEditEntityMessage(PacketType::EntityPhysics, tree, id, properties); - _entity->setLastBroadcast(now); // for debug/physics status icons // NOTE: we don't descend to children for ownership bid. Instead, if we win ownership of the parent // then in sendUpdate() we'll walk descendents and send updates for their QueryAACubes if necessary. diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 8ea4cdcd60..81a017a46d 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -96,7 +96,7 @@ void CauterizedModel::createRenderItemSet() { shapeID++; } } - _blendshapeBuffersInitialized = true; + _blendshapeOffsetsInitialized = true; } else { Model::createRenderItemSet(); } @@ -175,7 +175,7 @@ void CauterizedModel::updateClusterMatrices() { // post the blender if we're not currently waiting for one to finish auto modelBlender = DependencyManager::get(); - if (_blendshapeBuffersInitialized && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { + if (_blendshapeOffsetsInitialized && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; modelBlender->noteRequiresBlend(getThisPointer()); } diff --git a/libraries/render-utils/src/HighlightEffect.cpp b/libraries/render-utils/src/HighlightEffect.cpp index 2e51013bc5..2d4aee7880 100644 --- a/libraries/render-utils/src/HighlightEffect.cpp +++ b/libraries/render-utils/src/HighlightEffect.cpp @@ -39,10 +39,10 @@ namespace gr { extern void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state); -HighlightRessources::HighlightRessources() { +HighlightResources::HighlightResources() { } -void HighlightRessources::update(const gpu::FramebufferPointer& primaryFrameBuffer) { +void HighlightResources::update(const gpu::FramebufferPointer& primaryFrameBuffer) { auto newFrameSize = glm::ivec2(primaryFrameBuffer->getSize()); // If the buffer size changed, we need to delete our FBOs and recreate them at the @@ -58,32 +58,37 @@ void HighlightRessources::update(const gpu::FramebufferPointer& primaryFrameBuff if (!_colorFrameBuffer) { allocateColorBuffer(primaryFrameBuffer); } + + // The primaryFrameBuffer render buffer can change + if (_colorFrameBuffer->getRenderBuffer(0) != primaryFrameBuffer->getRenderBuffer(0)) { + _colorFrameBuffer->setRenderBuffer(0, primaryFrameBuffer->getRenderBuffer(0)); + } } } -void HighlightRessources::allocateColorBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) { +void HighlightResources::allocateColorBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) { _colorFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("primaryWithStencil")); _colorFrameBuffer->setRenderBuffer(0, primaryFrameBuffer->getRenderBuffer(0)); _colorFrameBuffer->setStencilBuffer(_depthStencilTexture, _depthStencilTexture->getTexelFormat()); } -void HighlightRessources::allocateDepthBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) { +void HighlightResources::allocateDepthBuffer(const gpu::FramebufferPointer& primaryFrameBuffer) { auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); _depthStencilTexture = gpu::TexturePointer(gpu::Texture::createRenderBuffer(depthFormat, _frameSize.x, _frameSize.y)); _depthFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("highlightDepth")); _depthFrameBuffer->setDepthStencilBuffer(_depthStencilTexture, depthFormat); } -gpu::FramebufferPointer HighlightRessources::getDepthFramebuffer() { +gpu::FramebufferPointer HighlightResources::getDepthFramebuffer() { assert(_depthFrameBuffer); return _depthFrameBuffer; } -gpu::TexturePointer HighlightRessources::getDepthTexture() { +gpu::TexturePointer HighlightResources::getDepthTexture() { return _depthStencilTexture; } -gpu::FramebufferPointer HighlightRessources::getColorFramebuffer() { +gpu::FramebufferPointer HighlightResources::getColorFramebuffer() { assert(_colorFrameBuffer); return _colorFrameBuffer; } @@ -97,25 +102,21 @@ float HighlightSharedParameters::getBlurPixelWidth(const render::HighlightStyle& } PrepareDrawHighlight::PrepareDrawHighlight() { - _ressources = std::make_shared(); + _resources = std::make_shared(); } void PrepareDrawHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { auto destinationFrameBuffer = inputs; - _ressources->update(destinationFrameBuffer); - outputs = _ressources; + _resources->update(destinationFrameBuffer); + outputs = _resources; } gpu::PipelinePointer DrawHighlightMask::_stencilMaskPipeline; gpu::PipelinePointer DrawHighlightMask::_stencilMaskFillPipeline; -DrawHighlightMask::DrawHighlightMask(unsigned int highlightIndex, - render::ShapePlumberPointer shapePlumber, HighlightSharedParametersPointer parameters) : - _highlightPassIndex{ highlightIndex }, - _shapePlumber { shapePlumber }, - _sharedParameters{ parameters } { -} +DrawHighlightMask::DrawHighlightMask(unsigned int highlightIndex, render::ShapePlumberPointer shapePlumber, + HighlightSharedParametersPointer parameters) : _highlightPassIndex(highlightIndex), _shapePlumber(shapePlumber), _sharedParameters(parameters) {} void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { assert(renderContext->args); @@ -126,13 +127,13 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c const int PARAMETERS_SLOT = 0; if (!_stencilMaskPipeline || !_stencilMaskFillPipeline) { - gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + gpu::StatePointer state = std::make_shared(); state->setDepthTest(true, false, gpu::LESS_EQUAL); - state->setStencilTest(true, 0xFF, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_ZERO, gpu::State::STENCIL_OP_REPLACE)); + state->setStencilTest(true, 0xFF, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_REPLACE)); state->setColorWriteMask(false, false, false, false); state->setCullMode(gpu::State::CULL_FRONT); - gpu::StatePointer fillState = gpu::StatePointer(new gpu::State()); + gpu::StatePointer fillState = std::make_shared(); fillState->setDepthTest(false, false, gpu::LESS_EQUAL); fillState->setStencilTest(true, 0xFF, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_REPLACE)); fillState->setColorWriteMask(false, false, false, false); @@ -151,7 +152,7 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c auto highlightId = _sharedParameters->_highlightIds[_highlightPassIndex]; if (!inShapes.empty() && !render::HighlightStage::isIndexInvalid(highlightId)) { - auto ressources = inputs.get1(); + auto resources = inputs.get1(); auto& highlight = highlightStage->getHighlight(highlightId); RenderArgs* args = renderContext->args; @@ -165,15 +166,11 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c // while stereo is enabled triggers a warning gpu::doInBatch("DrawHighlightMask::run::begin", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); - batch.setFramebuffer(ressources->getDepthFramebuffer()); + batch.setFramebuffer(resources->getDepthFramebuffer()); batch.clearDepthStencilFramebuffer(1.0f, 0); }); - glm::mat4 projMat; - Transform viewMat; const auto jitter = inputs.get2(); - args->getViewFrustum().evalProjectionMatrix(projMat); - args->getViewFrustum().evalViewTransform(viewMat); render::ItemBounds itemBounds; @@ -185,6 +182,10 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c auto maskDeformedDQPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withDeformed().withDualQuatSkinned()); // Setup camera, projection and viewport for all items + glm::mat4 projMat; + Transform viewMat; + args->getViewFrustum().evalProjectionMatrix(projMat); + args->getViewFrustum().evalViewTransform(viewMat); batch.setViewportTransform(args->_viewport); batch.setProjectionTransform(projMat); batch.setProjectionJitter(jitter.x, jitter.y); @@ -233,7 +234,7 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c const auto securityMargin = 2.0f; const float blurPixelWidth = 2.0f * securityMargin * HighlightSharedParameters::getBlurPixelWidth(highlight._style, args->_viewport.w); - const auto framebufferSize = ressources->getSourceFrameSize(); + const auto framebufferSize = resources->getSourceFrameSize(); const glm::vec2 highlightWidth = { blurPixelWidth / framebufferSize.x, blurPixelWidth / framebufferSize.y }; if (highlightWidth != _outlineWidth.get()) { @@ -241,11 +242,6 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c } gpu::doInBatch("DrawHighlightMask::run::end", args->_context, [&](gpu::Batch& batch) { - // Setup camera, projection and viewport for all items - batch.setViewportTransform(args->_viewport); - batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat); - // Draw stencil mask with object bounding boxes auto stencilPipeline = highlight._style.isFilled() ? _stencilMaskFillPipeline : _stencilMaskPipeline; batch.setPipeline(stencilPipeline); @@ -264,15 +260,14 @@ gpu::PipelinePointer DrawHighlight::_pipeline; gpu::PipelinePointer DrawHighlight::_pipelineFilled; DrawHighlight::DrawHighlight(unsigned int highlightIndex, HighlightSharedParametersPointer parameters) : - _highlightPassIndex{ highlightIndex }, - _sharedParameters{ parameters } { + _highlightPassIndex(highlightIndex), _sharedParameters(parameters) { } void DrawHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { auto highlightFrameBuffer = inputs.get1(); auto highlightRect = inputs.get3(); - if (highlightFrameBuffer && highlightRect.z>0 && highlightRect.w>0) { + if (highlightFrameBuffer && highlightRect.z > 0 && highlightRect.w > 0) { auto sceneDepthBuffer = inputs.get2(); const auto frameTransform = inputs.get0(); auto highlightedDepthTexture = highlightFrameBuffer->getDepthTexture(); @@ -334,10 +329,11 @@ void DrawHighlight::run(const render::RenderContextPointer& renderContext, const const gpu::PipelinePointer& DrawHighlight::getPipeline(const render::HighlightStyle& style) { if (!_pipeline) { - gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + gpu::StatePointer state = std::make_shared(); state->setDepthTest(gpu::State::DepthTest(false, false)); state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); state->setStencilTest(true, 0, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::EQUAL)); + state->setColorWriteMask(true, true, true, true); auto program = gpu::Shader::createProgram(shader::render_utils::program::highlight); _pipeline = gpu::Pipeline::create(program, state); @@ -364,10 +360,10 @@ void DebugHighlight::configure(const Config& config) { } void DebugHighlight::run(const render::RenderContextPointer& renderContext, const Inputs& input) { - const auto highlightRessources = input.get0(); + const auto highlightResources = input.get0(); const auto highlightRect = input.get1(); - if (_isDisplayEnabled && highlightRessources && highlightRect.z>0 && highlightRect.w>0) { + if (_isDisplayEnabled && highlightResources && highlightRect.z > 0 && highlightRect.w > 0) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); RenderArgs* args = renderContext->args; @@ -376,7 +372,7 @@ void DebugHighlight::run(const render::RenderContextPointer& renderContext, cons auto primaryFramebuffer = input.get3(); gpu::doInBatch("DebugHighlight::run", args->_context, [&](gpu::Batch& batch) { batch.setViewportTransform(args->_viewport); - batch.setFramebuffer(highlightRessources->getColorFramebuffer()); + batch.setFramebuffer(highlightResources->getColorFramebuffer()); const auto geometryBuffer = DependencyManager::get(); @@ -386,13 +382,13 @@ void DebugHighlight::run(const render::RenderContextPointer& renderContext, cons args->getViewFrustum().evalViewTransform(viewMat); batch.setProjectionTransform(projMat); batch.setProjectionJitter(jitter.x, jitter.y); - batch.setViewTransform(viewMat, true); + batch.setViewTransform(viewMat); batch.setModelTransform(Transform()); const glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f); batch.setPipeline(getDepthPipeline()); - batch.setResourceTexture(0, highlightRessources->getDepthTexture()); + batch.setResourceTexture(0, highlightResources->getDepthTexture()); const glm::vec2 bottomLeft(-1.0f, -1.0f); const glm::vec2 topRight(1.0f, 1.0f); geometryBuffer->renderQuad(batch, bottomLeft, topRight, color, _geometryDepthId); @@ -415,6 +411,7 @@ void DebugHighlight::initializePipelines() { auto state = std::make_shared(); state->setDepthTest(gpu::State::DepthTest(false, false)); state->setStencilTest(true, 0, gpu::State::StencilTest(OUTLINE_STENCIL_MASK, 0xFF, gpu::EQUAL)); + state->setColorWriteMask(true, true, true, true); const auto vs = gpu::Shader::createVertex(shader::render_utils::vertex::debug_deferred_buffer); @@ -512,7 +509,7 @@ void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, ren const auto highlightSelectionNames = task.addJob("SelectionToHighlight", sharedParameters); // Prepare for highlight group rendering. - const auto highlightRessources = task.addJob("PrepareHighlight", primaryFramebuffer); + const auto highlightResources = task.addJob("PrepareHighlight", primaryFramebuffer); render::Varying highlight0Rect; for (auto i = 0; i < HighlightSharedParameters::MAX_PASS_COUNT; i++) { @@ -532,7 +529,7 @@ void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, ren stream << "HighlightMask" << i; name = stream.str(); } - const auto drawMaskInputs = DrawHighlightMask::Inputs(sortedBounds, highlightRessources, jitter).asVarying(); + const auto drawMaskInputs = DrawHighlightMask::Inputs(sortedBounds, highlightResources, jitter).asVarying(); const auto highlightedRect = task.addJob(name, drawMaskInputs, i, shapePlumber, sharedParameters); if (i == 0) { highlight0Rect = highlightedRect; @@ -544,12 +541,12 @@ void DrawHighlightTask::build(JobModel& task, const render::Varying& inputs, ren stream << "HighlightEffect" << i; name = stream.str(); } - const auto drawHighlightInputs = DrawHighlight::Inputs(deferredFrameTransform, highlightRessources, sceneFrameBuffer, highlightedRect, primaryFramebuffer).asVarying(); + const auto drawHighlightInputs = DrawHighlight::Inputs(deferredFrameTransform, highlightResources, sceneFrameBuffer, highlightedRect, primaryFramebuffer).asVarying(); task.addJob(name, drawHighlightInputs, i, sharedParameters); } // Debug highlight - const auto debugInputs = DebugHighlight::Inputs(highlightRessources, const_cast(highlight0Rect), jitter, primaryFramebuffer).asVarying(); + const auto debugInputs = DebugHighlight::Inputs(highlightResources, const_cast(highlight0Rect), jitter, primaryFramebuffer).asVarying(); task.addJob("HighlightDebug", debugInputs); } diff --git a/libraries/render-utils/src/HighlightEffect.h b/libraries/render-utils/src/HighlightEffect.h index 32668c1ab6..933503fdb5 100644 --- a/libraries/render-utils/src/HighlightEffect.h +++ b/libraries/render-utils/src/HighlightEffect.h @@ -19,9 +19,9 @@ #include "DeferredFramebuffer.h" #include "DeferredFrameTransform.h" -class HighlightRessources { +class HighlightResources { public: - HighlightRessources(); + HighlightResources(); gpu::FramebufferPointer getDepthFramebuffer(); gpu::TexturePointer getDepthTexture(); @@ -44,7 +44,7 @@ protected: void allocateDepthBuffer(const gpu::FramebufferPointer& primaryFrameBuffer); }; -using HighlightRessourcesPointer = std::shared_ptr; +using HighlightResourcesPointer = std::shared_ptr; class HighlightSharedParameters { public: @@ -65,7 +65,7 @@ using HighlightSharedParametersPointer = std::shared_ptr; PrepareDrawHighlight(); @@ -74,7 +74,7 @@ public: private: - HighlightRessourcesPointer _ressources; + HighlightResourcesPointer _resources; }; @@ -112,8 +112,7 @@ private: class DrawHighlightMask { public: - - using Inputs = render::VaryingSet3; + using Inputs = render::VaryingSet3; using Outputs = glm::ivec4; using JobModel = render::Job::ModelIO; @@ -122,7 +121,6 @@ public: void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); protected: - unsigned int _highlightPassIndex; render::ShapePlumberPointer _shapePlumber; HighlightSharedParametersPointer _sharedParameters; @@ -136,7 +134,7 @@ protected: class DrawHighlight { public: - using Inputs = render::VaryingSet5; + using Inputs = render::VaryingSet5; using Config = render::Job::Config; using JobModel = render::Job::ModelI; @@ -163,11 +161,10 @@ private: class DebugHighlightConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(bool viewMask MEMBER viewMask NOTIFY dirty) + Q_PROPERTY(bool viewMask MEMBER viewMask NOTIFY dirty) public: - - bool viewMask{ false }; + bool viewMask { false }; signals: void dirty(); @@ -175,7 +172,7 @@ signals: class DebugHighlight { public: - using Inputs = render::VaryingSet4; + using Inputs = render::VaryingSet4; using Config = DebugHighlightConfig; using JobModel = render::Job::ModelI; diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index c5df2c3e01..2fe0368db2 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -209,6 +209,7 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in bool useDualQuaternionSkinning = model->getUseDualQuaternionSkinning(); auto& modelMesh = model->getGeometry()->getMeshes().at(_meshIndex); + _meshNumVertices = (int)modelMesh->getNumVertices(); const Model::MeshState& state = model->getMeshState(_meshIndex); updateMeshPart(modelMesh, partIndex); @@ -238,19 +239,12 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in initCache(model); - if (_isBlendShaped) { - auto buffer = model->_blendshapeBuffers.find(meshIndex); - if (buffer != model->_blendshapeBuffers.end()) { - _blendshapeBuffer = buffer->second; - } - } - #ifdef Q_OS_MAC - // On mac AMD, we specifically need to have a _blendshapeBuffer bound when using a deformed mesh pipeline + // On mac AMD, we specifically need to have a _meshBlendshapeBuffer bound when using a deformed mesh pipeline // it cannot be null otherwise we crash in the drawcall using a deformed pipeline with a skinned only (not blendshaped) mesh - if ((_isBlendShaped || _isSkinned) && !_blendshapeBuffer) { + if ((_isBlendShaped || _isSkinned)) { glm::vec4 data; - _blendshapeBuffer = std::make_shared(sizeof(glm::vec4), reinterpret_cast(&data)); + _meshBlendshapeBuffer = std::make_shared(sizeof(glm::vec4), reinterpret_cast(&data)); } #endif @@ -292,8 +286,7 @@ void ModelMeshPartPayload::updateClusterBuffer(const std::vector& clu if (!_clusterBuffer) { _clusterBuffer = std::make_shared(clusterMatrices.size() * sizeof(glm::mat4), (const gpu::Byte*) clusterMatrices.data()); - } - else { + } else { _clusterBuffer->setSubData(0, clusterMatrices.size() * sizeof(glm::mat4), (const gpu::Byte*) clusterMatrices.data()); } @@ -313,8 +306,7 @@ void ModelMeshPartPayload::updateClusterBuffer(const std::vector(clusterDualQuaternions.size() * sizeof(Model::TransformDualQuaternion), (const gpu::Byte*) clusterDualQuaternions.data()); - } - else { + } else { _clusterBuffer->setSubData(0, clusterDualQuaternions.size() * sizeof(Model::TransformDualQuaternion), (const gpu::Byte*) clusterDualQuaternions.data()); } @@ -403,8 +395,8 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) { batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); batch.setInputFormat((_drawMesh->getVertexFormat())); - if (_blendshapeBuffer) { - batch.setResourceBuffer(0, _blendshapeBuffer); + if (_meshBlendshapeBuffer) { + batch.setResourceBuffer(0, _meshBlendshapeBuffer); } batch.setInputStream(0, _drawMesh->getVertexStream()); } @@ -431,7 +423,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) { bindMesh(batch); // IF deformed pass the mesh key - auto drawcallInfo = (uint16_t) (((_isBlendShaped && args->_enableBlendshape) << 0) | ((_isSkinned && args->_enableSkinning) << 1)); + auto drawcallInfo = (uint16_t) (((_isBlendShaped && _meshBlendshapeBuffer && args->_enableBlendshape) << 0) | ((_isSkinned && args->_enableSkinning) << 1)); if (drawcallInfo) { batch.setDrawcallUniform(drawcallInfo); } @@ -483,3 +475,12 @@ void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& blendshapeBuffers, const QVector& blendedMeshSizes) { + if (_meshIndex < blendedMeshSizes.length() && blendedMeshSizes.at(_meshIndex) == _meshNumVertices) { + auto blendshapeBuffer = blendshapeBuffers.find(_meshIndex); + if (blendshapeBuffer != blendshapeBuffers.end()) { + _meshBlendshapeBuffer = blendshapeBuffer->second; + } + } +} diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index ceed4b330b..4d41d1d93e 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -134,10 +134,13 @@ public: bool _isBlendShaped { false }; bool _hasTangents { false }; + void setBlendshapeBuffer(const std::unordered_map& blendshapeBuffers, const QVector& blendedMeshSizes); + private: void initCache(const ModelPointer& model); - gpu::BufferPointer _blendshapeBuffer; + gpu::BufferPointer _meshBlendshapeBuffer; + int _meshNumVertices; render::ShapeKey _shapeKey { render::ShapeKey::Builder::invalid() }; }; diff --git a/libraries/render-utils/src/MetaModelPayload.cpp b/libraries/render-utils/src/MetaModelPayload.cpp new file mode 100644 index 0000000000..510972d86a --- /dev/null +++ b/libraries/render-utils/src/MetaModelPayload.cpp @@ -0,0 +1,55 @@ +// +// MetaModelPayload.cpp +// +// Created by Sam Gondelman on 10/9/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "MetaModelPayload.h" + +#include "AbstractViewStateInterface.h" +#include "MeshPartPayload.h" + +void MetaModelPayload::setBlendedVertices(int blendNumber, const QVector& blendshapeOffsets, const QVector& blendedMeshSizes, const render::ItemIDs& subRenderItems) { + PROFILE_RANGE(render, __FUNCTION__); + if (blendNumber < _appliedBlendNumber) { + return; + } + _appliedBlendNumber = blendNumber; + + // We have fewer meshes than before. Invalidate everything + if (blendedMeshSizes.length() < (int)_blendshapeBuffers.size()) { + _blendshapeBuffers.clear(); + } + + int index = 0; + for (int i = 0; i < blendedMeshSizes.size(); i++) { + int numVertices = blendedMeshSizes.at(i); + + // This mesh isn't blendshaped + if (numVertices == 0) { + _blendshapeBuffers.erase(i); + continue; + } + + const auto& buffer = _blendshapeBuffers.find(i); + const auto blendShapeBufferSize = numVertices * sizeof(BlendshapeOffset); + if (buffer == _blendshapeBuffers.end()) { + _blendshapeBuffers[i] = std::make_shared(blendShapeBufferSize, (gpu::Byte*) blendshapeOffsets.constData() + index * sizeof(BlendshapeOffset), blendShapeBufferSize); + } else { + buffer->second->setData(blendShapeBufferSize, (gpu::Byte*) blendshapeOffsets.constData() + index * sizeof(BlendshapeOffset)); + } + + index += numVertices; + } + + render::Transaction transaction; + for (auto& id : subRenderItems) { + transaction.updateItem(id, [this, blendedMeshSizes](ModelMeshPartPayload& data) { + data.setBlendshapeBuffer(_blendshapeBuffers, blendedMeshSizes); + }); + } + AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction); +} diff --git a/libraries/render-utils/src/MetaModelPayload.h b/libraries/render-utils/src/MetaModelPayload.h new file mode 100644 index 0000000000..83731f3039 --- /dev/null +++ b/libraries/render-utils/src/MetaModelPayload.h @@ -0,0 +1,30 @@ +// +// MetaModelPayload.h +// +// Created by Sam Gondelman on 10/9/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_MetaModelPayload_h +#define hifi_MetaModelPayload_h + +#include + +#include "Model.h" + +#include "gpu/Buffer.h" + +class MetaModelPayload { +public: + void setBlendedVertices(int blendNumber, const QVector& blendshapeOffsets, const QVector& blendedMeshSizes, const render::ItemIDs& subRenderItems); + +private: + std::unordered_map _blendshapeBuffers; + int _appliedBlendNumber { 0 }; + +}; + +#endif \ No newline at end of file diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index a700c200f8..53009e8bfa 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -62,8 +62,6 @@ Model::Model(QObject* parent, SpatiallyNestable* spatiallyNestableOverride) : _snapModelToRegistrationPoint(false), _snappedToRegistrationPoint(false), _url(HTTP_INVALID_COM), - _blendNumber(0), - _appliedBlendNumber(0), _isWireframe(false), _renderItemKeyGlobalFlags(render::ItemKey::Builder().withVisible().withTagBits(render::hifi::TAG_ALL_VIEWS).build()) { @@ -311,7 +309,7 @@ bool Model::updateGeometry() { initializeBlendshapes(mesh, i); i++; } - _blendshapeBuffersInitialized = true; + _blendshapeOffsetsInitialized = true; needFullUpdate = true; emit rigReady(); } @@ -979,7 +977,8 @@ const render::ItemKey Model::getRenderItemKeyGlobalFlags() const { bool Model::addToScene(const render::ScenePointer& scene, render::Transaction& transaction, - render::Item::Status::Getters& statusGetters) { + render::Item::Status::Getters& statusGetters, + BlendShapeOperator modelBlendshapeOperator) { if (!_addedToScene && isLoaded()) { updateClusterMatrices(); if (_modelMeshRenderItems.empty()) { @@ -987,10 +986,11 @@ bool Model::addToScene(const render::ScenePointer& scene, } } + _modelBlendshapeOperator = modelBlendshapeOperator; + bool somethingAdded = false; if (_modelMeshRenderItemsMap.empty()) { - bool hasTransparent = false; size_t verticesCount = 0; foreach(auto renderItem, _modelMeshRenderItems) { @@ -1032,9 +1032,8 @@ void Model::removeFromScene(const render::ScenePointer& scene, render::Transacti _modelMeshMaterialNames.clear(); _modelMeshRenderItemShapes.clear(); - _blendshapeBuffers.clear(); _blendshapeOffsets.clear(); - _blendshapeBuffersInitialized = false; + _blendshapeOffsetsInitialized = false; _addedToScene = false; @@ -1437,7 +1436,7 @@ void Model::updateClusterMatrices() { // post the blender if we're not currently waiting for one to finish auto modelBlender = DependencyManager::get(); - if (_blendshapeBuffersInitialized && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { + if (_blendshapeOffsetsInitialized && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; modelBlender->noteRequiresBlend(getThisPointer()); } @@ -1445,9 +1444,8 @@ void Model::updateClusterMatrices() { void Model::deleteGeometry() { _deleteGeometryCounter++; - _blendshapeBuffers.clear(); _blendshapeOffsets.clear(); - _blendshapeBuffersInitialized = false; + _blendshapeOffsetsInitialized = false; _meshStates.clear(); _rig.destroyAnimGraph(); _blendedBlendshapeCoefficients.clear(); @@ -1525,7 +1523,7 @@ void Model::createRenderItemSet() { shapeID++; } } - _blendshapeBuffersInitialized = true; + _blendshapeOffsetsInitialized = true; } bool Model::isRenderable() const { @@ -1654,6 +1652,7 @@ Blender::Blender(ModelPointer model, int blendNumber, const Geometry::WeakPointe void Blender::run() { QVector blendshapeOffsets; + QVector blendedMeshSizes; if (_model && _model->isLoaded()) { DETAILED_PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xFFFF0000, 0, { { "url", _model->getURL().toString() } }); int offset = 0; @@ -1662,12 +1661,22 @@ void Blender::run() { foreach(const FBXMesh& mesh, meshes) { auto modelMeshBlendshapeOffsets = _model->_blendshapeOffsets.find(meshIndex++); if (mesh.blendshapes.isEmpty() || modelMeshBlendshapeOffsets == _model->_blendshapeOffsets.end()) { + // Not blendshaped or not initialized + blendedMeshSizes.push_back(0); + continue; + } + + if (mesh.vertices.size() != modelMeshBlendshapeOffsets->second.size()) { + // Mesh sizes don't match. Something has gone wrong + blendedMeshSizes.push_back(0); continue; } blendshapeOffsets += modelMeshBlendshapeOffsets->second; BlendshapeOffset* meshBlendshapeOffsets = blendshapeOffsets.data() + offset; - offset += modelMeshBlendshapeOffsets->second.size(); + int numVertices = modelMeshBlendshapeOffsets->second.size(); + blendedMeshSizes.push_back(numVertices); + offset += numVertices; std::vector unpackedBlendshapeOffsets(modelMeshBlendshapeOffsets->second.size()); const float NORMAL_COEFFICIENT_SCALE = 0.01f; @@ -1711,7 +1720,7 @@ void Blender::run() { } // post the result to the ModelBlender, which will dispatch to the model if still alive QMetaObject::invokeMethod(DependencyManager::get().data(), "setBlendedVertices", - Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber), Q_ARG(QVector, blendshapeOffsets)); + Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber), Q_ARG(QVector, blendshapeOffsets), Q_ARG(QVector, blendedMeshSizes)); } bool Model::maybeStartBlender() { @@ -1722,43 +1731,18 @@ bool Model::maybeStartBlender() { return false; } -void Model::setBlendedVertices(int blendNumber, const QVector& blendshapeOffsets) { - PROFILE_RANGE(render, __FUNCTION__); - if (!isLoaded() || blendNumber < _appliedBlendNumber || !_blendshapeBuffersInitialized) { - return; - } - _appliedBlendNumber = blendNumber; - const FBXGeometry& fbxGeometry = getFBXGeometry(); - int index = 0; - for (int i = 0; i < fbxGeometry.meshes.size(); i++) { - const FBXMesh& mesh = fbxGeometry.meshes.at(i); - auto meshBlendshapeOffsets = _blendshapeOffsets.find(i); - const auto& buffer = _blendshapeBuffers.find(i); - if (mesh.blendshapes.isEmpty() || meshBlendshapeOffsets == _blendshapeOffsets.end() || buffer == _blendshapeBuffers.end()) { - continue; - } - - const auto blendshapeOffsetSize = meshBlendshapeOffsets->second.size() * sizeof(BlendshapeOffset); - buffer->second->setSubData(0, blendshapeOffsetSize, (gpu::Byte*) blendshapeOffsets.constData() + index * sizeof(BlendshapeOffset)); - - index += meshBlendshapeOffsets->second.size(); - } -} - void Model::initializeBlendshapes(const FBXMesh& mesh, int index) { if (mesh.blendshapes.empty()) { // mesh doesn't have blendshape, did we allocate one though ? - if (_blendshapeBuffers.find(index) != _blendshapeBuffers.end()) { - qWarning() << "Mesh does not have Blendshape yet a blendshapeOffset buffer is allocated ?"; + if (_blendshapeOffsets.find(index) != _blendshapeOffsets.end()) { + qWarning() << "Mesh does not have Blendshape yet the blendshapeOffsets are allocated ?"; } return; } // Mesh has blendshape, let s allocate the local buffer if not done yet - if (_blendshapeBuffers.find(index) == _blendshapeBuffers.end()) { + if (_blendshapeOffsets.find(index) == _blendshapeOffsets.end()) { QVector blendshapeOffset; blendshapeOffset.fill(BlendshapeOffset(), mesh.vertices.size()); - const auto blendshapeOffsetsSize = blendshapeOffset.size() * sizeof(BlendshapeOffset); - _blendshapeBuffers[index] = std::make_shared(blendshapeOffsetsSize, (const gpu::Byte*) blendshapeOffset.constData(), blendshapeOffsetsSize); _blendshapeOffsets[index] = blendshapeOffset; } } @@ -1791,10 +1775,14 @@ void ModelBlender::noteRequiresBlend(ModelPointer model) { } } -void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, QVector blendshapeOffsets) { +void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, QVector blendshapeOffsets, QVector blendedMeshSizes) { if (model) { - model->setBlendedVertices(blendNumber, blendshapeOffsets); + auto blendshapeOperator = model->getModelBlendshapeOperator(); + if (blendshapeOperator) { + blendshapeOperator(blendNumber, blendshapeOffsets, blendedMeshSizes, model->fetchRenderItemIDs()); + } } + { Lock lock(_mutex); _pendingBlenders--; diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index e42da4ecb1..71809821eb 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -86,6 +86,7 @@ struct BlendshapeOffsetUnpacked { }; using BlendshapeOffset = BlendshapeOffsetPacked; +using BlendShapeOperator = std::function&, const QVector&, const render::ItemIDs&)>; /// A generic 3D model displaying geometry loaded from a URL. class Model : public QObject, public std::enable_shared_from_this, public scriptable::ModelProvider { @@ -141,7 +142,14 @@ public: } bool addToScene(const render::ScenePointer& scene, render::Transaction& transaction, - render::Item::Status::Getters& statusGetters); + BlendShapeOperator modelBlendshapeOperator) { + auto getters = render::Item::Status::Getters(0); + return addToScene(scene, transaction, getters, modelBlendshapeOperator); + } + bool addToScene(const render::ScenePointer& scene, + render::Transaction& transaction, + render::Item::Status::Getters& statusGetters, + BlendShapeOperator modelBlendshapeOperator = nullptr); void removeFromScene(const render::ScenePointer& scene, render::Transaction& transaction); bool isRenderable() const; @@ -155,9 +163,6 @@ public: bool maybeStartBlender(); - /// Sets blended vertices computed in a separate thread. - void setBlendedVertices(int blendNumber, const QVector& blendshapeOffsets); - bool isLoaded() const { return (bool)_renderGeometry && _renderGeometry->isGeometryLoaded(); } bool isAddedToScene() const { return _addedToScene; } @@ -339,6 +344,7 @@ public: uint32_t getGeometryCounter() const { return _deleteGeometryCounter; } const QMap& getRenderItems() const { return _modelMeshRenderItemsMap; } + BlendShapeOperator getModelBlendshapeOperator() const { return _modelBlendshapeOperator; } void renderDebugMeshBoxes(gpu::Batch& batch); @@ -432,18 +438,13 @@ protected: virtual void deleteGeometry(); - QVector _blendshapeCoefficients; - QUrl _url; - std::unordered_map _blendshapeBuffers; - bool _blendshapeBuffersInitialized{ false }; - - QVector>> _dilatedTextures; - + BlendShapeOperator _modelBlendshapeOperator { nullptr }; + QVector _blendshapeCoefficients; QVector _blendedBlendshapeCoefficients; - int _blendNumber; - int _appliedBlendNumber; + int _blendNumber { 0 }; + bool _blendshapeOffsetsInitialized { false }; mutable QMutex _mutex{ QMutex::Recursive }; @@ -460,7 +461,6 @@ protected: // debug rendering support int _debugMeshBoxesID = GeometryCache::UNKNOWN_ID; - static AbstractViewStateInterface* _viewState; QVector> _modelMeshRenderItems; @@ -533,7 +533,7 @@ public: bool shouldComputeBlendshapes() { return _computeBlendshapes; } public slots: - void setBlendedVertices(ModelPointer model, int blendNumber, QVector blendshapeOffsets); + void setBlendedVertices(ModelPointer model, int blendNumber, QVector blendshapeOffsets, QVector blendedMeshSizes); void setComputeBlendshapes(bool computeBlendshapes) { _computeBlendshapes = computeBlendshapes; } private: diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 114ccab712..90015768d0 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -78,7 +78,7 @@ void SoftAttachmentModel::updateClusterMatrices() { // post the blender if we're not currently waiting for one to finish auto modelBlender = DependencyManager::get(); - if (_blendshapeBuffersInitialized && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { + if (_blendshapeOffsetsInitialized && modelBlender->shouldComputeBlendshapes() && geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) { _blendedBlendshapeCoefficients = _blendshapeCoefficients; modelBlender->noteRequiresBlend(getThisPointer()); } diff --git a/libraries/script-engine/src/ModelScriptingInterface.cpp b/libraries/script-engine/src/ModelScriptingInterface.cpp index c693083ebf..1716ea72ff 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.cpp +++ b/libraries/script-engine/src/ModelScriptingInterface.cpp @@ -192,7 +192,7 @@ QScriptValue ModelScriptingInterface::getVertex(MeshProxy* meshProxy, int vertex } glm::vec3 pos = vertexBufferView.get(vertexIndex); - return vec3toScriptValue(_modelScriptEngine, pos); + return vec3ToScriptValue(_modelScriptEngine, pos); } diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 8cfd2b75cf..be1a9c20ef 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -679,9 +679,9 @@ void ScriptEngine::init() { qScriptRegisterSequenceMetaType>(this); qScriptRegisterSequenceMetaType>(this); - qScriptRegisterSequenceMetaType >(this); - qScriptRegisterSequenceMetaType >(this); - qScriptRegisterSequenceMetaType >(this); + qScriptRegisterSequenceMetaType>(this); + qScriptRegisterSequenceMetaType>(this); + qScriptRegisterSequenceMetaType>(this); QScriptValue xmlHttpRequestConstructorValue = newFunction(XMLHttpRequestClass::constructor); globalObject().setProperty("XMLHttpRequest", xmlHttpRequestConstructorValue); diff --git a/libraries/script-engine/src/SpatialEvent.cpp b/libraries/script-engine/src/SpatialEvent.cpp index d06cc556d3..8520c0c485 100644 --- a/libraries/script-engine/src/SpatialEvent.cpp +++ b/libraries/script-engine/src/SpatialEvent.cpp @@ -33,9 +33,9 @@ SpatialEvent::SpatialEvent(const SpatialEvent& event) { QScriptValue SpatialEvent::toScriptValue(QScriptEngine* engine, const SpatialEvent& event) { QScriptValue obj = engine->newObject(); - obj.setProperty("locTranslation", vec3toScriptValue(engine, event.locTranslation) ); + obj.setProperty("locTranslation", vec3ToScriptValue(engine, event.locTranslation) ); obj.setProperty("locRotation", quatToScriptValue(engine, event.locRotation) ); - obj.setProperty("absTranslation", vec3toScriptValue(engine, event.absTranslation) ); + obj.setProperty("absTranslation", vec3ToScriptValue(engine, event.absTranslation) ); obj.setProperty("absRotation", quatToScriptValue(engine, event.absRotation) ); return obj; diff --git a/libraries/script-engine/src/TouchEvent.cpp b/libraries/script-engine/src/TouchEvent.cpp index 6ff591decf..58ac9ec8c1 100644 --- a/libraries/script-engine/src/TouchEvent.cpp +++ b/libraries/script-engine/src/TouchEvent.cpp @@ -14,9 +14,10 @@ #include #include -#include #include +#include "RegisteredMetaTypes.h" + TouchEvent::TouchEvent() : x(0.0f), y(0.0f), @@ -220,7 +221,7 @@ QScriptValue TouchEvent::toScriptValue(QScriptEngine* engine, const TouchEvent& QScriptValue pointsObj = engine->newArray(); int index = 0; foreach (glm::vec2 point, event.points) { - QScriptValue thisPoint = vec2toScriptValue(engine, point); + QScriptValue thisPoint = vec2ToScriptValue(engine, point); pointsObj.setProperty(index, thisPoint); index++; } diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h index 696981d1b4..fe903c07e2 100644 --- a/libraries/script-engine/src/Vec3.h +++ b/libraries/script-engine/src/Vec3.h @@ -21,24 +21,6 @@ #include "GLMHelpers.h" -/**jsdoc - * A 3-dimensional vector. See also the {@link Vec3(0)|Vec3} object. - * - * @typedef {object} Vec3 - * @property {number} x - X-coordinate of the vector. - * @property {number} y - Y-coordinate of the vector. - * @property {number} z - Z-coordinate of the vector. - */ - -/**jsdoc - * A color vector. See also the {@link Vec3(0)|Vec3} object. - * - * @typedef {object} Vec3Color - * @property {number} x - Red component value. Integer in the range 0 - 255. - * @property {number} y - Green component value. Integer in the range 0 - 255. - * @property {number} z - Blue component value. Integer in the range 0 - 255. - */ - /**jsdoc * The Vec3 API facilities for generating and manipulating 3-dimensional vectors. High Fidelity uses a right-handed * Cartesian coordinate system where the y-axis is the "up" and the negative z-axis is the "front" direction. diff --git a/libraries/shared/src/AtRestDetector.cpp b/libraries/shared/src/AtRestDetector.cpp index 4c08b30dcd..4b4356d641 100644 --- a/libraries/shared/src/AtRestDetector.cpp +++ b/libraries/shared/src/AtRestDetector.cpp @@ -30,32 +30,37 @@ void AtRestDetector::reset(const glm::vec3& startPosition, const glm::quat& star _isAtRest = false; } -bool AtRestDetector::update(const glm::vec3& position, const glm::quat& rotation) { - uint64_t now = usecTimestampNow(); - float dt = (float)(now - _lastUpdateTime) / (float)USECS_PER_SECOND; - _lastUpdateTime = now; - const float TAU = 1.0f; - float delta = glm::min(dt / TAU, 1.0f); +void AtRestDetector::update(const glm::vec3& position, const glm::quat& rotation) { + _lastIsAtRest = _isAtRest; + if (_isValid) { + uint64_t now = usecTimestampNow(); + float dt = (float)(now - _lastUpdateTime) / (float)USECS_PER_SECOND; + _lastUpdateTime = now; + const float TAU = 1.0f; + float delta = glm::min(dt / TAU, 1.0f); - // keep a running average of position. - _positionAverage = position * delta + _positionAverage * (1 - delta); + // keep a running average of position. + _positionAverage = position * delta + _positionAverage * (1 - delta); - // keep a running average of position variances. - glm::vec3 dx = position - _positionAverage; - _positionVariance = glm::dot(dx, dx) * delta + _positionVariance * (1 - delta); + // keep a running average of position variances. + glm::vec3 dx = position - _positionAverage; + _positionVariance = glm::dot(dx, dx) * delta + _positionVariance * (1 - delta); - // keep a running average of quaternion logarithms. - glm::quat quatLogAsQuat = glm::log(rotation); - glm::vec3 quatLog(quatLogAsQuat.x, quatLogAsQuat.y, quatLogAsQuat.z); - _quatLogAverage = quatLog * delta + _quatLogAverage * (1 - delta); + // keep a running average of quaternion logarithms. + glm::quat quatLogAsQuat = glm::log(rotation); + glm::vec3 quatLog(quatLogAsQuat.x, quatLogAsQuat.y, quatLogAsQuat.z); + _quatLogAverage = quatLog * delta + _quatLogAverage * (1 - delta); - // keep a running average of quatLog variances. - glm::vec3 dql = quatLog - _quatLogAverage; - _quatLogVariance = glm::dot(dql, dql) * delta + _quatLogVariance * (1 - delta); + // keep a running average of quatLog variances. + glm::vec3 dql = quatLog - _quatLogAverage; + _quatLogVariance = glm::dot(dql, dql) * delta + _quatLogVariance * (1 - delta); - const float POSITION_VARIANCE_THRESHOLD = 0.001f; - const float QUAT_LOG_VARIANCE_THRESHOLD = 0.00002f; + const float POSITION_VARIANCE_THRESHOLD = 0.001f; + const float QUAT_LOG_VARIANCE_THRESHOLD = 0.00002f; - _isAtRest = _positionVariance < POSITION_VARIANCE_THRESHOLD && _quatLogVariance < QUAT_LOG_VARIANCE_THRESHOLD; - return _isAtRest; + _isAtRest = _positionVariance < POSITION_VARIANCE_THRESHOLD && _quatLogVariance < QUAT_LOG_VARIANCE_THRESHOLD; + } else { + reset(position, rotation); + _isValid = true; + } } diff --git a/libraries/shared/src/AtRestDetector.h b/libraries/shared/src/AtRestDetector.h index 525156de65..eb8e5f53f9 100644 --- a/libraries/shared/src/AtRestDetector.h +++ b/libraries/shared/src/AtRestDetector.h @@ -17,21 +17,27 @@ class AtRestDetector { public: + AtRestDetector() {}; AtRestDetector(const glm::vec3& startPosition, const glm::quat& startRotation); void reset(const glm::vec3& startPosition, const glm::quat& startRotation); // returns true if object is at rest, dt in assumed to be seconds. - bool update(const glm::vec3& position, const glm::quat& startRotation); + void update(const glm::vec3& position, const glm::quat& startRotation); + void invalidate() { _isValid = false; } bool isAtRest() const { return _isAtRest; } + bool onRest() const { return !_lastIsAtRest && _isAtRest; } + bool onWake() const { return _lastIsAtRest && !_isAtRest; } protected: + bool _isValid { false }; glm::vec3 _positionAverage; glm::vec3 _quatLogAverage; uint64_t _lastUpdateTime { 0 }; float _positionVariance { 0.0f }; float _quatLogVariance { 0.0f }; bool _isAtRest { false }; + bool _lastIsAtRest { false }; }; #endif diff --git a/libraries/shared/src/BufferParser.h b/libraries/shared/src/BufferParser.h index 74b47cb72f..c73356558e 100644 --- a/libraries/shared/src/BufferParser.h +++ b/libraries/shared/src/BufferParser.h @@ -94,13 +94,6 @@ inline void BufferParser::readValue(QUuid& result) { readUuid(result); } -template<> -inline void BufferParser::readValue(xColor& result) { - readValue(result.red); - readValue(result.blue); - readValue(result.green); -} - template<> inline void BufferParser::readValue(QVector& result) { uint16_t length; readValue(length); diff --git a/libraries/shared/src/ColorUtils.h b/libraries/shared/src/ColorUtils.h index e113449db3..dd9fd8dcd6 100644 --- a/libraries/shared/src/ColorUtils.h +++ b/libraries/shared/src/ColorUtils.h @@ -22,7 +22,7 @@ extern const float srgbToLinearLookupTable[256]; class ColorUtils { public: - inline static glm::vec3 toVec3(const xColor& color); + inline static glm::vec3 toVec3(const glm::u8vec3& color); // Convert to gamma 2.2 space from linear inline static glm::vec3 toGamma22Vec3(const glm::vec3& linear); @@ -40,9 +40,9 @@ public: inline static float tosRGBFloat(const float& linear); }; -inline glm::vec3 ColorUtils::toVec3(const xColor& color) { +inline glm::vec3 ColorUtils::toVec3(const glm::u8vec3& color) { const float ONE_OVER_255 = 1.0f / 255.0f; - return glm::vec3(color.red * ONE_OVER_255, color.green * ONE_OVER_255, color.blue * ONE_OVER_255); + return glm::vec3(color.x * ONE_OVER_255, color.y * ONE_OVER_255, color.z * ONE_OVER_255); } inline glm::vec3 ColorUtils::toGamma22Vec3(const glm::vec3& linear) { diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index d324e5af10..1a62227c31 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -448,17 +448,16 @@ glm::vec2 toGlm(const QPointF& pt) { return glm::vec2(pt.x(), pt.y()); } -glm::vec3 toGlm(const xColor& color) { +glm::vec3 toGlm(const glm::u8vec3& color) { static const float MAX_COLOR = 255.0f; - return glm::vec3(color.red, color.green, color.blue) / MAX_COLOR; + return glm::vec3(color) / MAX_COLOR; } -xColor xColorFromGlm(const glm::vec3 & color) { +vec4 toGlm(const glm::u8vec3& color, float alpha) { static const float MAX_COLOR = 255.0f; - return { (uint8_t)(color.x * MAX_COLOR), (uint8_t)(color.y * MAX_COLOR), (uint8_t)(color.z * MAX_COLOR) }; + return vec4(glm::vec3(color) / MAX_COLOR, alpha); } - glm::vec4 toGlm(const QColor& color) { return glm::vec4(color.redF(), color.greenF(), color.blueF(), color.alphaF()); } @@ -475,10 +474,6 @@ QSize fromGlm(const glm::ivec2 & v) { return QSize(v.x, v.y); } -vec4 toGlm(const xColor& color, float alpha) { - return vec4((float)color.red / 255.0f, (float)color.green / 255.0f, (float)color.blue / 255.0f, alpha); -} - QRectF glmToRect(const glm::vec2 & pos, const glm::vec2 & size) { QRectF result(pos.x, pos.y, size.x, size.y); return result; diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 96219ea48c..e7aaace1ae 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -24,6 +24,7 @@ using glm::ivec2; using glm::ivec3; using glm::ivec4; using glm::uvec2; +using glm::u8vec3; using glm::uvec3; using glm::uvec4; using glm::mat3; @@ -174,12 +175,10 @@ bool isSimilarPosition(const glm::vec3& positionA, const glm::vec3& positionB, f uvec2 toGlm(const QSize& size); ivec2 toGlm(const QPoint& pt); vec2 toGlm(const QPointF& pt); -vec3 toGlm(const xColor& color); +vec3 toGlm(const glm::u8vec3& color); vec4 toGlm(const QColor& color); ivec4 toGlm(const QRect& rect); -vec4 toGlm(const xColor& color, float alpha); - -xColor xColorFromGlm(const glm::vec3 & c); +vec4 toGlm(const glm::u8vec3& color, float alpha); QSize fromGlm(const glm::ivec2 & v); QMatrix4x4 fromGlm(const glm::mat4 & m); diff --git a/libraries/shared/src/QVariantGLM.cpp b/libraries/shared/src/QVariantGLM.cpp index f7fd7fbc8a..12c4628fbd 100644 --- a/libraries/shared/src/QVariantGLM.cpp +++ b/libraries/shared/src/QVariantGLM.cpp @@ -20,10 +20,6 @@ QVariantList quatToQList(const glm::quat& g) { return QVariantList() << g.x << g.y << g.z << g.w; } -QVariantList rgbColorToQList(const rgbColor& v) { - return QVariantList() << (int)(v[0]) << (int)(v[1]) << (int)(v[2]); -} - QVariantMap vec3ToQMap(const glm::vec3& glmVector) { QVariantMap vectorAsVariantMap; vectorAsVariantMap["x"] = glmVector.x; @@ -56,14 +52,6 @@ glm::quat qListToQuat(const QVariant& q) { return glm::quat(w, x, y, z); } -void qListToRgbColor(const QVariant& q, rgbColor& returnValue) { - QVariantList qList = q.toList(); - returnValue[RED_INDEX] = qList[RED_INDEX].toInt(); - returnValue[GREEN_INDEX] = qList[GREEN_INDEX].toInt(); - returnValue[BLUE_INDEX] = qList[BLUE_INDEX].toInt(); -} - - glm::vec3 qMapToVec3(const QVariant& q) { QVariantMap qMap = q.toMap(); if (qMap.contains("x") && qMap.contains("y") && qMap.contains("z")) { diff --git a/libraries/shared/src/QVariantGLM.h b/libraries/shared/src/QVariantGLM.h index a8f8b531c0..d61f64312a 100644 --- a/libraries/shared/src/QVariantGLM.h +++ b/libraries/shared/src/QVariantGLM.h @@ -19,14 +19,12 @@ QVariantList vec3ToQList(const glm::vec3& g); QVariantList quatToQList(const glm::quat& g); -QVariantList rgbColorToQList(const rgbColor& v); QVariantMap vec3ToQMap(const glm::vec3& glmVector); QVariantMap quatToQMap(const glm::quat& glmQuat); glm::vec3 qListToVec3(const QVariant& q); glm::quat qListToQuat(const QVariant& q); -void qListToRgbColor(const QVariant& q, rgbColor& returnValue); glm::vec3 qMapToVec3(const QVariant& q); glm::quat qMapToQuat(const QVariant& q); diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 0c8f4f0466..dc84afff93 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -25,14 +25,14 @@ #include #include -int vec4MetaTypeId = qRegisterMetaType(); +int vec2MetaTypeId = qRegisterMetaType(); +int u8vec3MetaTypeId = qRegisterMetaType(); int vec3MetaTypeId = qRegisterMetaType(); +int vec4MetaTypeId = qRegisterMetaType(); int qVectorVec3MetaTypeId = qRegisterMetaType>(); int qVectorQuatMetaTypeId = qRegisterMetaType>(); int qVectorBoolMetaTypeId = qRegisterMetaType>(); -int vec2MetaTypeId = qRegisterMetaType(); int quatMetaTypeId = qRegisterMetaType(); -int xColorMetaTypeId = qRegisterMetaType(); int pickRayMetaTypeId = qRegisterMetaType(); int collisionMetaTypeId = qRegisterMetaType(); int qMapURLStringMetaTypeId = qRegisterMetaType>(); @@ -41,27 +41,560 @@ int voidLambdaType = qRegisterMetaType>(); int variantLambdaType = qRegisterMetaType>(); void registerMetaTypes(QScriptEngine* engine) { - qScriptRegisterMetaType(engine, mat4toScriptValue, mat4FromScriptValue); + qScriptRegisterMetaType(engine, vec2ToScriptValue, vec2FromScriptValue); + qScriptRegisterMetaType(engine, vec3ToScriptValue, vec3FromScriptValue); + qScriptRegisterMetaType(engine, u8vec3ToScriptValue, u8vec3FromScriptValue); qScriptRegisterMetaType(engine, vec4toScriptValue, vec4FromScriptValue); - qScriptRegisterMetaType(engine, vec3toScriptValue, vec3FromScriptValue); + qScriptRegisterMetaType(engine, quatToScriptValue, quatFromScriptValue); + qScriptRegisterMetaType(engine, mat4toScriptValue, mat4FromScriptValue); + qScriptRegisterMetaType(engine, qVectorVec3ToScriptValue, qVectorVec3FromScriptValue); qScriptRegisterMetaType(engine, qVectorQuatToScriptValue, qVectorQuatFromScriptValue); qScriptRegisterMetaType(engine, qVectorBoolToScriptValue, qVectorBoolFromScriptValue); qScriptRegisterMetaType(engine, qVectorFloatToScriptValue, qVectorFloatFromScriptValue); qScriptRegisterMetaType(engine, qVectorIntToScriptValue, qVectorIntFromScriptValue); - qScriptRegisterMetaType(engine, vec2toScriptValue, vec2FromScriptValue); - qScriptRegisterMetaType(engine, quatToScriptValue, quatFromScriptValue); + + qScriptRegisterMetaType(engine, qSizeFToScriptValue, qSizeFFromScriptValue); qScriptRegisterMetaType(engine, qRectToScriptValue, qRectFromScriptValue); - qScriptRegisterMetaType(engine, xColorToScriptValue, xColorFromScriptValue); - qScriptRegisterMetaType(engine, qColorToScriptValue, qColorFromScriptValue); qScriptRegisterMetaType(engine, qURLToScriptValue, qURLFromScriptValue); + qScriptRegisterMetaType(engine, qColorToScriptValue, qColorFromScriptValue); + qScriptRegisterMetaType(engine, pickRayToScriptValue, pickRayFromScriptValue); qScriptRegisterMetaType(engine, collisionToScriptValue, collisionFromScriptValue); qScriptRegisterMetaType(engine, quuidToScriptValue, quuidFromScriptValue); - qScriptRegisterMetaType(engine, qSizeFToScriptValue, qSizeFFromScriptValue); qScriptRegisterMetaType(engine, aaCubeToScriptValue, aaCubeFromScriptValue); } +QScriptValue vec2ToScriptValue(QScriptEngine* engine, const glm::vec2& vec2) { + auto prototype = engine->globalObject().property("__hifi_vec2__"); + if (!prototype.property("defined").toBool()) { + prototype = engine->evaluate( + "__hifi_vec2__ = Object.defineProperties({}, { " + "defined: { value: true }," + "0: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "1: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," + "u: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "v: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }" + "})" + ); + } + QScriptValue value = engine->newObject(); + value.setProperty("x", vec2.x); + value.setProperty("y", vec2.y); + value.setPrototype(prototype); + return value; +} + +void vec2FromScriptValue(const QScriptValue& object, glm::vec2& vec2) { + if (object.isNumber()) { + vec2 = glm::vec2(object.toVariant().toFloat()); + } else if (object.isArray()) { + QVariantList list = object.toVariant().toList(); + if (list.length() == 2) { + vec2.x = list[0].toFloat(); + vec2.y = list[1].toFloat(); + } + } else { + QScriptValue x = object.property("x"); + if (!x.isValid()) { + x = object.property("u"); + } + + QScriptValue y = object.property("y"); + if (!y.isValid()) { + y = object.property("v"); + } + + vec2.x = x.toVariant().toFloat(); + vec2.y = y.toVariant().toFloat(); + } +} + +QVariant vec2ToVariant(const glm::vec2 &vec2) { + if (vec2.x != vec2.x || vec2.y != vec2.y) { + // if vec2 contains a NaN don't try to convert it + return QVariant(); + } + QVariantMap result; + result["x"] = vec2.x; + result["y"] = vec2.y; + return result; +} + +glm::vec2 vec2FromVariant(const QVariant &object, bool& isValid) { + isValid = false; + glm::vec2 result; + if (object.canConvert()) { + result = glm::vec2(object.toFloat()); + isValid = true; + } else if (object.canConvert()) { + auto qvec2 = qvariant_cast(object); + result.x = qvec2.x(); + result.y = qvec2.y(); + isValid = true; + } else { + auto map = object.toMap(); + auto x = map["x"]; + if (!x.isValid()) { + x = map["u"]; + } + + auto y = map["y"]; + if (!y.isValid()) { + y = map["v"]; + } + + if (x.isValid() && y.isValid()) { + result.x = x.toFloat(&isValid); + if (isValid) { + result.y = y.toFloat(&isValid); + } + } + } + return result; +} + +glm::vec2 vec2FromVariant(const QVariant &object) { + bool valid; + return vec2FromVariant(object, valid); +} + +QScriptValue vec3ToScriptValue(QScriptEngine* engine, const glm::vec3& vec3) { + auto prototype = engine->globalObject().property("__hifi_vec3__"); + if (!prototype.property("defined").toBool()) { + prototype = engine->evaluate( + "__hifi_vec3__ = Object.defineProperties({}, { " + "defined: { value: true }," + "0: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "1: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," + "2: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }," + "r: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "g: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," + "b: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }," + "red: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "green: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," + "blue: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }" + "})" + ); + } + QScriptValue value = engine->newObject(); + value.setProperty("x", vec3.x); + value.setProperty("y", vec3.y); + value.setProperty("z", vec3.z); + value.setPrototype(prototype); + return value; +} + +QScriptValue vec3ColorToScriptValue(QScriptEngine* engine, const glm::vec3& vec3) { + auto prototype = engine->globalObject().property("__hifi_vec3_color__"); + if (!prototype.property("defined").toBool()) { + prototype = engine->evaluate( + "__hifi_vec3_color__ = Object.defineProperties({}, { " + "defined: { value: true }," + "0: { set: function(nv) { return this.red = nv; }, get: function() { return this.red; } }," + "1: { set: function(nv) { return this.green = nv; }, get: function() { return this.green; } }," + "2: { set: function(nv) { return this.blue = nv; }, get: function() { return this.blue; } }," + "r: { set: function(nv) { return this.red = nv; }, get: function() { return this.red; } }," + "g: { set: function(nv) { return this.green = nv; }, get: function() { return this.green; } }," + "b: { set: function(nv) { return this.blue = nv; }, get: function() { return this.blue; } }," + "x: { set: function(nv) { return this.red = nv; }, get: function() { return this.red; } }," + "y: { set: function(nv) { return this.green = nv; }, get: function() { return this.green; } }," + "z: { set: function(nv) { return this.blue = nv; }, get: function() { return this.blue; } }" + "})" + ); + } + QScriptValue value = engine->newObject(); + value.setProperty("red", vec3.x); + value.setProperty("green", vec3.y); + value.setProperty("blue", vec3.z); + value.setPrototype(prototype); + return value; +} + +void vec3FromScriptValue(const QScriptValue& object, glm::vec3& vec3) { + if (object.isNumber()) { + vec3 = glm::vec3(object.toVariant().toFloat()); + } else if (object.isString()) { + QColor qColor(object.toString()); + if (qColor.isValid()) { + vec3.x = qColor.red(); + vec3.y = qColor.green(); + vec3.z = qColor.blue(); + } + } else if (object.isArray()) { + QVariantList list = object.toVariant().toList(); + if (list.length() == 3) { + vec3.x = list[0].toFloat(); + vec3.y = list[1].toFloat(); + vec3.z = list[2].toFloat(); + } + } else { + QScriptValue x = object.property("x"); + if (!x.isValid()) { + x = object.property("r"); + } + if (!x.isValid()) { + x = object.property("red"); + } + + QScriptValue y = object.property("y"); + if (!y.isValid()) { + y = object.property("g"); + } + if (!y.isValid()) { + y = object.property("green"); + } + + QScriptValue z = object.property("z"); + if (!z.isValid()) { + z = object.property("b"); + } + if (!z.isValid()) { + z = object.property("blue"); + } + + vec3.x = x.toVariant().toFloat(); + vec3.y = y.toVariant().toFloat(); + vec3.z = z.toVariant().toFloat(); + } +} + +QScriptValue u8vec3ToScriptValue(QScriptEngine* engine, const glm::u8vec3& vec3) { + auto prototype = engine->globalObject().property("__hifi_u8vec3__"); + if (!prototype.property("defined").toBool()) { + prototype = engine->evaluate( + "__hifi_u8vec3__ = Object.defineProperties({}, { " + "defined: { value: true }," + "0: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "1: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," + "2: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }," + "r: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "g: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," + "b: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }," + "red: { set: function(nv) { return this.x = nv; }, get: function() { return this.x; } }," + "green: { set: function(nv) { return this.y = nv; }, get: function() { return this.y; } }," + "blue: { set: function(nv) { return this.z = nv; }, get: function() { return this.z; } }" + "})" + ); + } + QScriptValue value = engine->newObject(); + value.setProperty("x", vec3.x); + value.setProperty("y", vec3.y); + value.setProperty("z", vec3.z); + value.setPrototype(prototype); + return value; +} + +QScriptValue u8vec3ColorToScriptValue(QScriptEngine* engine, const glm::u8vec3& vec3) { + auto prototype = engine->globalObject().property("__hifi_u8vec3_color__"); + if (!prototype.property("defined").toBool()) { + prototype = engine->evaluate( + "__hifi_u8vec3_color__ = Object.defineProperties({}, { " + "defined: { value: true }," + "0: { set: function(nv) { return this.red = nv; }, get: function() { return this.red; } }," + "1: { set: function(nv) { return this.green = nv; }, get: function() { return this.green; } }," + "2: { set: function(nv) { return this.blue = nv; }, get: function() { return this.blue; } }," + "r: { set: function(nv) { return this.red = nv; }, get: function() { return this.red; } }," + "g: { set: function(nv) { return this.green = nv; }, get: function() { return this.green; } }," + "b: { set: function(nv) { return this.blue = nv; }, get: function() { return this.blue; } }," + "x: { set: function(nv) { return this.red = nv; }, get: function() { return this.red; } }," + "y: { set: function(nv) { return this.green = nv; }, get: function() { return this.green; } }," + "z: { set: function(nv) { return this.blue = nv; }, get: function() { return this.blue; } }" + "})" + ); + } + QScriptValue value = engine->newObject(); + value.setProperty("red", vec3.x); + value.setProperty("green", vec3.y); + value.setProperty("blue", vec3.z); + value.setPrototype(prototype); + return value; +} + +void u8vec3FromScriptValue(const QScriptValue& object, glm::u8vec3& vec3) { + if (object.isNumber()) { + vec3 = glm::vec3(object.toVariant().toUInt()); + } else if (object.isString()) { + QColor qColor(object.toString()); + if (qColor.isValid()) { + vec3.x = (uint8_t)qColor.red(); + vec3.y = (uint8_t)qColor.green(); + vec3.z = (uint8_t)qColor.blue(); + } + } else if (object.isArray()) { + QVariantList list = object.toVariant().toList(); + if (list.length() == 3) { + vec3.x = list[0].toUInt(); + vec3.y = list[1].toUInt(); + vec3.z = list[2].toUInt(); + } + } else { + QScriptValue x = object.property("x"); + if (!x.isValid()) { + x = object.property("r"); + } + if (!x.isValid()) { + x = object.property("red"); + } + + QScriptValue y = object.property("y"); + if (!y.isValid()) { + y = object.property("g"); + } + if (!y.isValid()) { + y = object.property("green"); + } + + QScriptValue z = object.property("z"); + if (!z.isValid()) { + z = object.property("b"); + } + if (!z.isValid()) { + z = object.property("blue"); + } + + vec3.x = x.toVariant().toUInt(); + vec3.y = y.toVariant().toUInt(); + vec3.z = z.toVariant().toUInt(); + } +} + +QVariant vec3toVariant(const glm::vec3& vec3) { + if (vec3.x != vec3.x || vec3.y != vec3.y || vec3.z != vec3.z) { + // if vec3 contains a NaN don't try to convert it + return QVariant(); + } + QVariantMap result; + result["x"] = vec3.x; + result["y"] = vec3.y; + result["z"] = vec3.z; + return result; +} + +glm::vec3 vec3FromVariant(const QVariant& object, bool& valid) { + glm::vec3 v; + valid = false; + if (!object.isValid() || object.isNull()) { + return v; + } else if (object.canConvert()) { + v = glm::vec3(object.toFloat()); + valid = true; + } else if (object.canConvert()) { + auto qvec3 = qvariant_cast(object); + v.x = qvec3.x(); + v.y = qvec3.y(); + v.z = qvec3.z(); + valid = true; + } else if (object.canConvert()) { + QColor qColor(object.toString()); + if (qColor.isValid()) { + v.x = (uint8_t)qColor.red(); + v.y = (uint8_t)qColor.green(); + v.z = (uint8_t)qColor.blue(); + valid = true; + } + } else if (object.canConvert()) { + QColor qColor = qvariant_cast(object); + if (qColor.isValid()) { + v.x = (uint8_t)qColor.red(); + v.y = (uint8_t)qColor.green(); + v.z = (uint8_t)qColor.blue(); + valid = true; + } + } else { + auto map = object.toMap(); + auto x = map["x"]; + if (!x.isValid()) { + x = map["r"]; + } + if (!x.isValid()) { + x = map["red"]; + } + + auto y = map["y"]; + if (!y.isValid()) { + y = map["g"]; + } + if (!y.isValid()) { + y = map["green"]; + } + + auto z = map["z"]; + if (!z.isValid()) { + z = map["b"]; + } + if (!z.isValid()) { + z = map["blue"]; + } + + if (x.canConvert() && y.canConvert() && z.canConvert()) { + v.x = x.toFloat(); + v.y = y.toFloat(); + v.z = z.toFloat(); + valid = true; + } + } + return v; +} + +glm::vec3 vec3FromVariant(const QVariant& object) { + bool valid = false; + return vec3FromVariant(object, valid); +} + +QVariant u8vec3toVariant(const glm::u8vec3& vec3) { + QVariantMap result; + result["x"] = vec3.x; + result["y"] = vec3.y; + result["z"] = vec3.z; + return result; +} + +QVariant u8vec3ColortoVariant(const glm::u8vec3& vec3) { + QVariantMap result; + result["red"] = vec3.x; + result["green"] = vec3.y; + result["blue"] = vec3.z; + return result; +} + +glm::u8vec3 u8vec3FromVariant(const QVariant& object, bool& valid) { + glm::u8vec3 v; + valid = false; + if (!object.isValid() || object.isNull()) { + return v; + } else if (object.canConvert()) { + v = glm::vec3(object.toUInt()); + valid = true; + } else if (object.canConvert()) { + auto qvec3 = qvariant_cast(object); + v.x = (uint8_t)qvec3.x(); + v.y = (uint8_t)qvec3.y(); + v.z = (uint8_t)qvec3.z(); + valid = true; + } else if (object.canConvert()) { + QColor qColor(object.toString()); + if (qColor.isValid()) { + v.x = (uint8_t)qColor.red(); + v.y = (uint8_t)qColor.green(); + v.z = (uint8_t)qColor.blue(); + valid = true; + } + } else if (object.canConvert()) { + QColor qColor = qvariant_cast(object); + if (qColor.isValid()) { + v.x = (uint8_t)qColor.red(); + v.y = (uint8_t)qColor.green(); + v.z = (uint8_t)qColor.blue(); + valid = true; + } + } else { + auto map = object.toMap(); + auto x = map["x"]; + if (!x.isValid()) { + x = map["r"]; + } + if (!x.isValid()) { + x = map["red"]; + } + + auto y = map["y"]; + if (!y.isValid()) { + y = map["g"]; + } + if (!y.isValid()) { + y = map["green"]; + } + + auto z = map["z"]; + if (!z.isValid()) { + z = map["b"]; + } + if (!z.isValid()) { + z = map["blue"]; + } + + if (x.canConvert() && y.canConvert() && z.canConvert()) { + v.x = x.toUInt(); + v.y = y.toUInt(); + v.z = z.toUInt(); + valid = true; + } + } + return v; +} + +glm::u8vec3 u8vec3FromVariant(const QVariant& object) { + bool valid = false; + return u8vec3FromVariant(object, valid); +} + +QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4) { + QScriptValue obj = engine->newObject(); + obj.setProperty("x", vec4.x); + obj.setProperty("y", vec4.y); + obj.setProperty("z", vec4.z); + obj.setProperty("w", vec4.w); + return obj; +} + +void vec4FromScriptValue(const QScriptValue& object, glm::vec4& vec4) { + vec4.x = object.property("x").toVariant().toFloat(); + vec4.y = object.property("y").toVariant().toFloat(); + vec4.z = object.property("z").toVariant().toFloat(); + vec4.w = object.property("w").toVariant().toFloat(); +} + +QVariant vec4toVariant(const glm::vec4& vec4) { + if (isNaN(vec4.x) || isNaN(vec4.y) || isNaN(vec4.z) || isNaN(vec4.w)) { + // if vec4 contains a NaN don't try to convert it + return QVariant(); + } + QVariantMap result; + result["x"] = vec4.x; + result["y"] = vec4.y; + result["z"] = vec4.z; + result["w"] = vec4.w; + return result; +} + +glm::vec4 vec4FromVariant(const QVariant& object, bool& valid) { + glm::vec4 v; + valid = false; + if (!object.isValid() || object.isNull()) { + return v; + } else if (object.canConvert()) { + v = glm::vec4(object.toFloat()); + valid = true; + } else if (object.canConvert()) { + auto qvec4 = qvariant_cast(object); + v.x = qvec4.x(); + v.y = qvec4.y(); + v.z = qvec4.z(); + v.w = qvec4.w(); + valid = true; + } else { + auto map = object.toMap(); + auto x = map["x"]; + auto y = map["y"]; + auto z = map["z"]; + auto w = map["w"]; + if (x.canConvert() && y.canConvert() && z.canConvert() && w.canConvert()) { + v.x = x.toFloat(); + v.y = y.toFloat(); + v.z = z.toFloat(); + v.w = w.toFloat(); + valid = true; + } + } + return v; +} + +glm::vec4 vec4FromVariant(const QVariant& object) { + bool valid = false; + return vec4FromVariant(object, valid); +} + QScriptValue mat4toScriptValue(QScriptEngine* engine, const glm::mat4& mat4) { QScriptValue obj = engine->newObject(); obj.setProperty("r0c0", mat4[0][0]); @@ -102,168 +635,42 @@ void mat4FromScriptValue(const QScriptValue& object, glm::mat4& mat4) { mat4[3][3] = object.property("r3c3").toVariant().toFloat(); } -QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4) { - QScriptValue obj = engine->newObject(); - obj.setProperty("x", vec4.x); - obj.setProperty("y", vec4.y); - obj.setProperty("z", vec4.z); - obj.setProperty("w", vec4.w); - return obj; -} - -void vec4FromScriptValue(const QScriptValue& object, glm::vec4& vec4) { - vec4.x = object.property("x").toVariant().toFloat(); - vec4.y = object.property("y").toVariant().toFloat(); - vec4.z = object.property("z").toVariant().toFloat(); - vec4.w = object.property("w").toVariant().toFloat(); -} - -QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3) { - QScriptValue obj = engine->newObject(); - if (vec3.x != vec3.x || vec3.y != vec3.y || vec3.z != vec3.z) { - // if vec3 contains a NaN don't try to convert it - return obj; +QScriptValue qVectorVec3ColorToScriptValue(QScriptEngine* engine, const QVector& vector) { + QScriptValue array = engine->newArray(); + for (int i = 0; i < vector.size(); i++) { + array.setProperty(i, vec3ColorToScriptValue(engine, vector.at(i))); } - obj.setProperty("x", vec3.x); - obj.setProperty("y", vec3.y); - obj.setProperty("z", vec3.z); - obj.setProperty("red", vec3.x); - obj.setProperty("green", vec3.y); - obj.setProperty("blue", vec3.z); - return obj; -} - -void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3) { - auto x = object.property("x").toVariant(); - if (!x.isValid()) { - x = object.property("red").toVariant(); - } - auto y = object.property("y").toVariant(); - if (!y.isValid()) { - y = object.property("green").toVariant(); - } - auto z = object.property("z").toVariant(); - if (!z.isValid()) { - z = object.property("blue").toVariant(); - } - vec3.x = x.toFloat(); - vec3.y = y.toFloat(); - vec3.z = z.toFloat(); -} - -QVariant vec3toVariant(const glm::vec3& vec3) { - if (vec3.x != vec3.x || vec3.y != vec3.y || vec3.z != vec3.z) { - // if vec3 contains a NaN don't try to convert it - return QVariant(); - } - QVariantMap result; - result["x"] = vec3.x; - result["y"] = vec3.y; - result["z"] = vec3.z; - return result; -} - -QVariant vec4toVariant(const glm::vec4& vec4) { - if (isNaN(vec4.x) || isNaN(vec4.y) || isNaN(vec4.z) || isNaN(vec4.w)) { - // if vec4 contains a NaN don't try to convert it - return QVariant(); - } - QVariantMap result; - result["x"] = vec4.x; - result["y"] = vec4.y; - result["z"] = vec4.z; - result["w"] = vec4.w; - return result; + return array; } QScriptValue qVectorVec3ToScriptValue(QScriptEngine* engine, const QVector& vector) { QScriptValue array = engine->newArray(); for (int i = 0; i < vector.size(); i++) { - array.setProperty(i, vec3toScriptValue(engine, vector.at(i))); + array.setProperty(i, vec3ToScriptValue(engine, vector.at(i))); } return array; } +QVector qVectorVec3FromScriptValue(const QScriptValue& array) { + QVector newVector; + int length = array.property("length").toInteger(); -glm::vec3 vec3FromVariant(const QVariant& object, bool& valid) { - glm::vec3 v; - valid = false; - if (!object.isValid() || object.isNull()) { - return v; - } else if (object.canConvert()) { - v = glm::vec3(object.toFloat()); - valid = true; - } else if (object.canConvert()) { - auto qvec3 = qvariant_cast(object); - v.x = qvec3.x(); - v.y = qvec3.y(); - v.z = qvec3.z(); - valid = true; - } else { - auto map = object.toMap(); - auto x = map["x"]; - auto y = map["y"]; - auto z = map["z"]; - if (!x.isValid()) { - x = map["width"]; - } - if (!y.isValid()) { - y = map["height"]; - } - if (!y.isValid()) { - z = map["depth"]; - } - - if (x.canConvert() && y.canConvert() && z.canConvert()) { - v.x = x.toFloat(); - v.y = y.toFloat(); - v.z = z.toFloat(); - valid = true; - } + for (int i = 0; i < length; i++) { + glm::vec3 newVec3 = glm::vec3(); + vec3FromScriptValue(array.property(i), newVec3); + newVector << newVec3; } - return v; + return newVector; } -glm::vec3 vec3FromVariant(const QVariant& object) { - bool valid = false; - return vec3FromVariant(object, valid); -} +void qVectorVec3FromScriptValue(const QScriptValue& array, QVector& vector) { + int length = array.property("length").toInteger(); -glm::vec4 vec4FromVariant(const QVariant& object, bool& valid) { - glm::vec4 v; - valid = false; - if (!object.isValid() || object.isNull()) { - return v; - } else if (object.canConvert()) { - v = glm::vec4(object.toFloat()); - valid = true; - } else if (object.canConvert()) { - auto qvec4 = qvariant_cast(object); - v.x = qvec4.x(); - v.y = qvec4.y(); - v.z = qvec4.z(); - v.w = qvec4.w(); - valid = true; - } else { - auto map = object.toMap(); - auto x = map["x"]; - auto y = map["y"]; - auto z = map["z"]; - auto w = map["w"]; - if (x.canConvert() && y.canConvert() && z.canConvert() && w.canConvert()) { - v.x = x.toFloat(); - v.y = y.toFloat(); - v.z = z.toFloat(); - v.w = w.toFloat(); - valid = true; - } + for (int i = 0; i < length; i++) { + glm::vec3 newVec3 = glm::vec3(); + vec3FromScriptValue(array.property(i), newVec3); + vector << newVec3; } - return v; -} - -glm::vec4 vec4FromVariant(const QVariant& object) { - bool valid = false; - return vec4FromVariant(object, valid); } QScriptValue quatToScriptValue(QScriptEngine* engine, const glm::quat& quat) { @@ -297,11 +704,11 @@ void quatFromScriptValue(const QScriptValue& object, glm::quat &quat) { glm::quat quatFromVariant(const QVariant &object, bool& isValid) { glm::quat q; if (object.canConvert()) { - auto qvec3 = qvariant_cast(object); - q.x = qvec3.x(); - q.y = qvec3.y(); - q.z = qvec3.z(); - q.w = qvec3.scalar(); + auto qquat = qvariant_cast(object); + q.x = qquat.x(); + q.y = qquat.y(); + q.z = qquat.z(); + q.w = qquat.scalar(); // enforce normalized quaternion float length = glm::length(q); @@ -340,7 +747,7 @@ glm::quat quatFromVariant(const QVariant& object) { QVariant quatToVariant(const glm::quat& quat) { if (quat.x != quat.x || quat.y != quat.y || quat.z != quat.z) { - // if vec3 contains a NaN don't try to convert it + // if quat contains a NaN don't try to convert it return QVariant(); } QVariantMap result; @@ -432,29 +839,6 @@ void qVectorIntFromScriptValue(const QScriptValue& array, QVector& vec } } -// -QVector qVectorVec3FromScriptValue(const QScriptValue& array){ - QVector newVector; - int length = array.property("length").toInteger(); - - for (int i = 0; i < length; i++) { - glm::vec3 newVec3 = glm::vec3(); - vec3FromScriptValue(array.property(i), newVec3); - newVector << newVec3; - } - return newVector; -} - -void qVectorVec3FromScriptValue(const QScriptValue& array, QVector& vector ) { - int length = array.property("length").toInteger(); - - for (int i = 0; i < length; i++) { - glm::vec3 newVec3 = glm::vec3(); - vec3FromScriptValue(array.property(i), newVec3); - vector << newVec3; - } -} - QVector qVectorQuatFromScriptValue(const QScriptValue& array){ QVector newVector; int length = array.property("length").toInteger(); @@ -495,71 +879,6 @@ void qVectorBoolFromScriptValue(const QScriptValue& array, QVector& vector } } -QScriptValue vec2toScriptValue(QScriptEngine* engine, const glm::vec2 &vec2) { - QScriptValue obj = engine->newObject(); - obj.setProperty("x", vec2.x); - obj.setProperty("y", vec2.y); - return obj; -} - -void vec2FromScriptValue(const QScriptValue &object, glm::vec2 &vec2) { - vec2.x = object.property("x").toVariant().toFloat(); - vec2.y = object.property("y").toVariant().toFloat(); -} - -QVariant vec2toVariant(const glm::vec2 &vec2) { - if (vec2.x != vec2.x || vec2.y != vec2.y) { - // if vec2 contains a NaN don't try to convert it - return QVariant(); - } - QVariantMap result; - result["x"] = vec2.x; - result["y"] = vec2.y; - return result; -} - -glm::vec2 vec2FromVariant(const QVariant &object, bool& isValid) { - isValid = false; - glm::vec2 result; - if (object.canConvert()) { - result = glm::vec2(object.toFloat()); - } else if (object.canConvert()) { - auto qvec2 = qvariant_cast(object); - result.x = qvec2.x(); - result.y = qvec2.y(); - } else { - auto map = object.toMap(); - auto x = map["x"]; - if (!x.isValid()) { - x = map["width"]; - } - auto y = map["y"]; - if (!y.isValid()) { - y = map["height"]; - } - if (x.isValid() && y.isValid()) { - result.x = x.toFloat(&isValid); - if (isValid) { - result.y = y.toFloat(&isValid); - } - } - } - return result; -} - -glm::vec2 vec2FromVariant(const QVariant &object) { - bool valid; - return vec2FromVariant(object, valid); -} - -/**jsdoc - * Defines a rectangular portion of an image or screen, or similar. - * @typedef {object} Rect - * @property {number} x - Left, x-coordinate value. - * @property {number} y - Top, y-coordinate value. - * @property {number} width - Width of the rectangle. - * @property {number} height - Height of the rectangle. - */ QScriptValue qRectToScriptValue(QScriptEngine* engine, const QRect& rect) { QScriptValue obj = engine->newObject(); obj.setProperty("x", rect.x()); @@ -654,94 +973,6 @@ QRectF qRectFFromVariant(const QVariant& object) { return qRectFFromVariant(object, valid); } - -QScriptValue xColorToScriptValue(QScriptEngine *engine, const xColor& color) { - QScriptValue obj = engine->newObject(); - obj.setProperty("red", color.red); - obj.setProperty("green", color.green); - obj.setProperty("blue", color.blue); - return obj; -} - -void xColorFromScriptValue(const QScriptValue &object, xColor& color) { - if (!object.isValid()) { - return; - } - if (object.isNumber()) { - color.red = color.green = color.blue = (uint8_t)object.toUInt32(); - } else if (object.isString()) { - QColor qcolor(object.toString()); - if (qcolor.isValid()) { - color.red = (uint8_t)qcolor.red(); - color.blue = (uint8_t)qcolor.blue(); - color.green = (uint8_t)qcolor.green(); - } - } else { - color.red = object.property("red").toVariant().toInt(); - color.green = object.property("green").toVariant().toInt(); - color.blue = object.property("blue").toVariant().toInt(); - } -} - -/**jsdoc - * An RGB color value. - * @typedef {object} Color - * @property {number} red - Red component value. Integer in the range 0 - 255. - * @property {number} green - Green component value. Integer in the range 0 - 255. - * @property {number} blue - Blue component value. Integer in the range 0 - 255. - */ -QVariant xColorToVariant(const xColor& color) { - QVariantMap obj; - obj["red"] = color.red; - obj["green"] = color.green; - obj["blue"] = color.blue; - return obj; -} - -xColor xColorFromVariant(const QVariant &object, bool& isValid) { - isValid = false; - xColor color { 0, 0, 0 }; - if (!object.isValid()) { - return color; - } - if (object.canConvert()) { - isValid = true; - color.red = color.green = color.blue = (uint8_t)object.toInt(); - } else if (object.canConvert()) { - QColor qcolor(object.toString()); - if (qcolor.isValid()) { - isValid = true; - color.red = (uint8_t)qcolor.red(); - color.blue = (uint8_t)qcolor.blue(); - color.green = (uint8_t)qcolor.green(); - } - } else if (object.canConvert()) { - QColor qcolor = qvariant_cast(object); - if (qcolor.isValid()) { - isValid = true; - color.red = (uint8_t)qcolor.red(); - color.blue = (uint8_t)qcolor.blue(); - color.green = (uint8_t)qcolor.green(); - } - } else { - QVariantMap map = object.toMap(); - color.red = map["red"].toInt(&isValid); - if (isValid) { - color.green = map["green"].toInt(&isValid); - } - if (isValid) { - color.blue = map["blue"].toInt(&isValid); - } - } - return color; -} - -xColor xColorFromVariant(const QVariant &object) { - bool valid; - return xColorFromVariant(object, valid); -} - - QScriptValue qColorToScriptValue(QScriptEngine* engine, const QColor& color) { QScriptValue object = engine->newObject(); object.setProperty("red", color.red()); @@ -804,9 +1035,9 @@ void qURLFromScriptValue(const QScriptValue& object, QUrl& url) { QScriptValue pickRayToScriptValue(QScriptEngine* engine, const PickRay& pickRay) { QScriptValue obj = engine->newObject(); - QScriptValue origin = vec3toScriptValue(engine, pickRay.origin); + QScriptValue origin = vec3ToScriptValue(engine, pickRay.origin); obj.setProperty("origin", origin); - QScriptValue direction = vec3toScriptValue(engine, pickRay.direction); + QScriptValue direction = vec3ToScriptValue(engine, pickRay.direction); obj.setProperty("direction", direction); return obj; } @@ -850,9 +1081,9 @@ QScriptValue collisionToScriptValue(QScriptEngine* engine, const Collision& coll obj.setProperty("type", collision.type); obj.setProperty("idA", quuidToScriptValue(engine, collision.idA)); obj.setProperty("idB", quuidToScriptValue(engine, collision.idB)); - obj.setProperty("penetration", vec3toScriptValue(engine, collision.penetration)); - obj.setProperty("contactPoint", vec3toScriptValue(engine, collision.contactPoint)); - obj.setProperty("velocityChange", vec3toScriptValue(engine, collision.velocityChange)); + obj.setProperty("penetration", vec3ToScriptValue(engine, collision.penetration)); + obj.setProperty("contactPoint", vec3ToScriptValue(engine, collision.contactPoint)); + obj.setProperty("velocityChange", vec3ToScriptValue(engine, collision.velocityChange)); return obj; } diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 9379269d13..64a874f63d 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -29,13 +29,12 @@ class QColor; class QUrl; -Q_DECLARE_METATYPE(glm::vec4) -Q_DECLARE_METATYPE(glm::vec3) Q_DECLARE_METATYPE(glm::vec2) +Q_DECLARE_METATYPE(glm::u8vec3) +Q_DECLARE_METATYPE(glm::vec3) +Q_DECLARE_METATYPE(glm::vec4) Q_DECLARE_METATYPE(glm::quat) Q_DECLARE_METATYPE(glm::mat4) -Q_DECLARE_METATYPE(xColor) -Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(AACube) Q_DECLARE_METATYPE(std::function); @@ -47,6 +46,101 @@ void registerMetaTypes(QScriptEngine* engine); QScriptValue mat4toScriptValue(QScriptEngine* engine, const glm::mat4& mat4); void mat4FromScriptValue(const QScriptValue& object, glm::mat4& mat4); +/**jsdoc +* A 2-dimensional vector. +* +* @typedef {object} Vec2 +* @property {number} x - X-coordinate of the vector. Synonyms: u. +* @property {number} y - Y-coordinate of the vector. Synonyms: v. +* @example Vec2s can be set in multiple ways and modified with their aliases, but still stringify in the same way +* Entities.editEntity(, { materialMappingPos: { x: 0.1, y: 0.2 }}); // { x: 0.1, y: 0.2 } +* Entities.editEntity(, { materialMappingPos: { u: 0.3, v: 0.4 }}); // { x: 0.3, y: 0.4 } +* Entities.editEntity(, { materialMappingPos: [0.5, 0.6] }); // { x: 0.5, y: 0.6 } +* Entities.editEntity(, { materialMappingPos: 0.7 }); // { x: 0.7, y: 0.7 } +* var color = Entities.getEntityProperties().materialMappingPos; // { x: 0.7, y: 0.7 } +* color.v = 0.8; // { x: 0.7, y: 0.8 } +*/ +QScriptValue vec2ToScriptValue(QScriptEngine* engine, const glm::vec2& vec2); +void vec2FromScriptValue(const QScriptValue& object, glm::vec2& vec2); + +QVariant vec2ToVariant(const glm::vec2& vec2); +glm::vec2 vec2FromVariant(const QVariant& object, bool& valid); +glm::vec2 vec2FromVariant(const QVariant& object); + +/**jsdoc +* A 3-dimensional vector. See also the {@link Vec3(0)|Vec3} object. +* +* @typedef {object} Vec3 +* @property {number} x - X-coordinate of the vector. Synonyms: r, red. +* @property {number} y - Y-coordinate of the vector. Synonyms: g, green. +* @property {number} z - Z-coordinate of the vector. Synonyms: b, blue. +* @example Vec3s can be set in multiple ways and modified with their aliases, but still stringify in the same way +* Entities.editEntity(, { position: { x: 1, y: 2, z: 3 }}); // { x: 1, y: 2, z: 3 } +* Entities.editEntity(, { position: { r: 4, g: 5, b: 6 }}); // { x: 4, y: 5, z: 6 } +* Entities.editEntity(, { position: { red: 7, green: 8, blue: 9 }}); // { x: 7, y: 8, z: 9 } +* Entities.editEntity(, { position: [10, 11, 12] }); // { x: 10, y: 11, z: 12 } +* Entities.editEntity(, { position: 13 }); // { x: 13, y: 13, z: 13 } +* var position = Entities.getEntityProperties().position; // { x: 13, y: 13, z: 13 } +* position.g = 14; // { x: 13, y: 14, z: 13 } +* position.blue = 15; // { x: 13, y: 14, z: 15 } +* Entities.editEntity(, { position: "red"}); // { x: 255, y: 0, z: 0 } +* Entities.editEntity(, { position: "#00FF00"}); // { x: 0, y: 255, z: 0 } +*/ +QScriptValue vec3ToScriptValue(QScriptEngine* engine, const glm::vec3& vec3); +QScriptValue vec3ColorToScriptValue(QScriptEngine* engine, const glm::vec3& vec3); +void vec3FromScriptValue(const QScriptValue& object, glm::vec3& vec3); + +QVariant vec3toVariant(const glm::vec3& vec3); +glm::vec3 vec3FromVariant(const QVariant &object, bool& valid); +glm::vec3 vec3FromVariant(const QVariant &object); + +/**jsdoc +* A color vector. See also the {@link Vec3(0)|Vec3} object. +* +* @typedef {object} Color +* @property {number} red - Red component value. Integer in the range 0 - 255. Synonyms: r, x. +* @property {number} green - Green component value. Integer in the range 0 - 255. Synonyms: g, y. +* @property {number} blue - Blue component value. Integer in the range 0 - 255. Synonyms: b, z. +* @example Colors can be set in multiple ways and modified with their aliases, but still stringify in the same way +* Entities.editEntity(, { color: { x: 1, y: 2, z: 3 }}); // { red: 1, green: 2, blue: 3 } +* Entities.editEntity(, { color: { r: 4, g: 5, b: 6 }}); // { red: 4, green: 5, blue: 6 } +* Entities.editEntity(, { color: { red: 7, green: 8, blue: 9 }}); // { red: 7, green: 8, blue: 9 } +* Entities.editEntity(, { color: [10, 11, 12] }); // { red: 10, green: 11, blue: 12 } +* Entities.editEntity(, { color: 13 }); // { red: 13, green: 13, blue: 13 } +* var color = Entities.getEntityProperties().color; // { red: 13, green: 13, blue: 13 } +* color.g = 14; // { red: 13, green: 14, blue: 13 } +* color.blue = 15; // { red: 13, green: 14, blue: 15 } +* Entities.editEntity(, { color: "red"}); // { red: 255, green: 0, blue: 0 } +* Entities.editEntity(, { color: "#00FF00"}); // { red: 0, green: 255, blue: 0 } +*/ +/**jsdoc +* A color vector. See also the {@link Vec3(0)|Vec3} object. +* +* @typedef {object} ColorFloat +* @property {number} red - Red component value. Real in the range 0 - 255. Synonyms: r, x. +* @property {number} green - Green component value. Real in the range 0 - 255. Synonyms: g, y. +* @property {number} blue - Blue component value. Real in the range 0 - 255. Synonyms: b, z. +* @example ColorFloats can be set in multiple ways and modified with their aliases, but still stringify in the same way +* Entities.editEntity(, { color: { x: 1, y: 2, z: 3 }}); // { red: 1, green: 2, blue: 3 } +* Entities.editEntity(, { color: { r: 4, g: 5, b: 6 }}); // { red: 4, green: 5, blue: 6 } +* Entities.editEntity(, { color: { red: 7, green: 8, blue: 9 }}); // { red: 7, green: 8, blue: 9 } +* Entities.editEntity(, { color: [10, 11, 12] }); // { red: 10, green: 11, blue: 12 } +* Entities.editEntity(, { color: 13 }); // { red: 13, green: 13, blue: 13 } +* var color = Entities.getEntityProperties().color; // { red: 13, green: 13, blue: 13 } +* color.g = 14; // { red: 13, green: 14, blue: 13 } +* color.blue = 15; // { red: 13, green: 14, blue: 15 } +* Entities.editEntity(, { color: "red"}); // { red: 255, green: 0, blue: 0 } +* Entities.editEntity(, { color: "#00FF00"}); // { red: 0, green: 255, blue: 0 } +*/ +QScriptValue u8vec3ToScriptValue(QScriptEngine* engine, const glm::u8vec3& vec3); +QScriptValue u8vec3ColorToScriptValue(QScriptEngine* engine, const glm::u8vec3& vec3); +void u8vec3FromScriptValue(const QScriptValue& object, glm::u8vec3& vec3); + +QVariant u8vec3toVariant(const glm::u8vec3& vec3); +QVariant u8vec3ColortoVariant(const glm::u8vec3& vec3); +glm::u8vec3 u8vec3FromVariant(const QVariant &object, bool& valid); +glm::u8vec3 u8vec3FromVariant(const QVariant &object); + /**jsdoc * A 4-dimensional vector. * @@ -56,36 +150,12 @@ void mat4FromScriptValue(const QScriptValue& object, glm::mat4& mat4); * @property {number} z - Z-coordinate of the vector. * @property {number} w - W-coordinate of the vector. */ -// Vec4 QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4); void vec4FromScriptValue(const QScriptValue& object, glm::vec4& vec4); QVariant vec4toVariant(const glm::vec4& vec4); glm::vec4 vec4FromVariant(const QVariant &object, bool& valid); glm::vec4 vec4FromVariant(const QVariant &object); -// Vec3 -QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3); -void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3); - -QVariant vec3toVariant(const glm::vec3& vec3); -glm::vec3 vec3FromVariant(const QVariant &object, bool& valid); -glm::vec3 vec3FromVariant(const QVariant &object); - -/**jsdoc - * A 2-dimensional vector. - * - * @typedef {object} Vec2 - * @property {number} x - X-coordinate of the vector. - * @property {number} y - Y-coordinate of the vector. - */ -// Vec2 -QScriptValue vec2toScriptValue(QScriptEngine* engine, const glm::vec2 &vec2); -void vec2FromScriptValue(const QScriptValue &object, glm::vec2 &vec2); - -QVariant vec2toVariant(const glm::vec2 &vec2); -glm::vec2 vec2FromVariant(const QVariant &object, bool& valid); -glm::vec2 vec2FromVariant(const QVariant &object); - // Quaternions QScriptValue quatToScriptValue(QScriptEngine* engine, const glm::quat& quat); void quatFromScriptValue(const QScriptValue &object, glm::quat& quat); @@ -94,25 +164,26 @@ QVariant quatToVariant(const glm::quat& quat); glm::quat quatFromVariant(const QVariant &object, bool& isValid); glm::quat quatFromVariant(const QVariant &object); -// Rect +/**jsdoc + * Defines a rectangular portion of an image or screen, or similar. + * @typedef {object} Rect + * @property {number} x - Left, x-coordinate value. + * @property {number} y - Top, y-coordinate value. + * @property {number} width - Width of the rectangle. + * @property {number} height - Height of the rectangle. + */ QScriptValue qRectToScriptValue(QScriptEngine* engine, const QRect& rect); void qRectFromScriptValue(const QScriptValue& object, QRect& rect); QRect qRectFromVariant(const QVariant& object, bool& isValid); QRect qRectFromVariant(const QVariant& object); QVariant qRectToVariant(const QRect& rect); + QScriptValue qRectFToScriptValue(QScriptEngine* engine, const QRectF& rect); void qRectFFromScriptValue(const QScriptValue& object, QRectF& rect); QRectF qRectFFromVariant(const QVariant& object, bool& isValid); QRectF qRectFFromVariant(const QVariant& object); QVariant qRectFToVariant(const QRectF& rect); -// xColor -QScriptValue xColorToScriptValue(QScriptEngine* engine, const xColor& color); -void xColorFromScriptValue(const QScriptValue &object, xColor& color); - -QVariant xColorToVariant(const xColor& color); -xColor xColorFromVariant(const QVariant &object, bool& isValid); - // QColor QScriptValue qColorToScriptValue(QScriptEngine* engine, const QColor& color); void qColorFromScriptValue(const QScriptValue& object, QColor& color); @@ -121,11 +192,14 @@ QScriptValue qURLToScriptValue(QScriptEngine* engine, const QUrl& url); void qURLFromScriptValue(const QScriptValue& object, QUrl& url); // vector +Q_DECLARE_METATYPE(QVector) QScriptValue qVectorVec3ToScriptValue(QScriptEngine* engine, const QVector& vector); +QScriptValue qVectorVec3ColorToScriptValue(QScriptEngine* engine, const QVector& vector); void qVectorVec3FromScriptValue(const QScriptValue& array, QVector& vector); QVector qVectorVec3FromScriptValue(const QScriptValue& array); // vector +Q_DECLARE_METATYPE(QVector) QScriptValue qVectorQuatToScriptValue(QScriptEngine* engine, const QVector& vector); void qVectorQuatFromScriptValue(const QScriptValue& array, QVector& vector); QVector qVectorQuatFromScriptValue(const QScriptValue& array); diff --git a/libraries/shared/src/SharedUtil.cpp b/libraries/shared/src/SharedUtil.cpp index 012e7aa1f5..39def1cab9 100644 --- a/libraries/shared/src/SharedUtil.cpp +++ b/libraries/shared/src/SharedUtil.cpp @@ -24,7 +24,6 @@ #include - #ifdef Q_OS_WIN #include #include "CPUIdent.h" @@ -348,7 +347,7 @@ unsigned char* pointToVoxel(float x, float y, float z, float s, unsigned char r, } auto voxelSizeInBytes = bytesRequiredForCodeLength(voxelSizeInOctets); // (voxelSizeInBits/8)+1; - auto voxelBufferSize = voxelSizeInBytes + sizeof(rgbColor); // 3 for color + auto voxelBufferSize = voxelSizeInBytes + sizeof(glm::u8vec3); // 3 for color // allocate our resulting buffer unsigned char* voxelOut = new unsigned char[voxelBufferSize]; diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index 3b24110f18..f36574bed6 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -80,43 +80,6 @@ const int BYTES_PER_COLOR = 3; const int BYTES_PER_FLAGS = 1; typedef unsigned char colorPart; typedef unsigned char nodeColor[BYTES_PER_COLOR + BYTES_PER_FLAGS]; -typedef unsigned char rgbColor[BYTES_PER_COLOR]; - -inline QDebug& operator<<(QDebug& dbg, const rgbColor& c) { - dbg.nospace() << "{type='rgbColor'" - ", red=" << c[0] << - ", green=" << c[1] << - ", blue=" << c[2] << - "}"; - return dbg; -} - -struct xColor { - xColor() {} - xColor(unsigned char r, unsigned char g, unsigned char b) : red(r), green(g), blue(b) {} - unsigned char red; - unsigned char green; - unsigned char blue; -}; - -inline QDebug& operator<<(QDebug& dbg, const xColor& c) { - dbg.nospace() << "{type='xColor'" - ", red=" << c.red << - ", green=" << c.green << - ", blue=" << c.blue << - "}"; - return dbg; -} - -inline bool operator==(const xColor& lhs, const xColor& rhs) -{ - return (lhs.red == rhs.red) && (lhs.green == rhs.green) && (lhs.blue == rhs.blue); -} - -inline bool operator!=(const xColor& lhs, const xColor& rhs) -{ - return (lhs.red != rhs.red) || (lhs.green != rhs.green) || (lhs.blue != rhs.blue); -} // Use a custom User-Agent to avoid ModSecurity filtering, e.g. by hosting providers. const QByteArray HIGH_FIDELITY_USER_AGENT = "Mozilla/5.0 (HighFidelityInterface)"; diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 4b8768704a..fd2ff6e790 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -75,7 +75,10 @@ void SpatiallyNestable::setParentID(const QUuid& parentID) { }); bool success = false; - getParentPointer(success); + auto parent = getParentPointer(success); + if (success && parent) { + parent->updateQueryAACube(); + } } Transform SpatiallyNestable::getParentTransform(bool& success, int depth) const { @@ -155,12 +158,14 @@ void SpatiallyNestable::beParentOfChild(SpatiallyNestablePointer newChild) const _childrenLock.withWriteLock([&] { _children[newChild->getID()] = newChild; }); + // Our QueryAACube will automatically be updated to include our new child } void SpatiallyNestable::forgetChild(SpatiallyNestablePointer newChild) const { _childrenLock.withWriteLock([&] { _children.remove(newChild->getID()); }); + _queryAACubeSet = false; // We need to reset our queryAACube when we lose a child } void SpatiallyNestable::setParentJointIndex(quint16 parentJointIndex) { @@ -1092,7 +1097,23 @@ AACube SpatiallyNestable::getMaximumAACube(bool& success) const { return AACube(getWorldPosition(success) - glm::vec3(defaultAACubeSize / 2.0f), defaultAACubeSize); } -const float PARENTED_EXPANSION_FACTOR = 3.0f; +AACube SpatiallyNestable::calculateInitialQueryAACube(bool& success) { + success = false; + AACube maxAACube = getMaximumAACube(success); + if (!success) { + return AACube(); + } + + success = true; + if (shouldPuffQueryAACube()) { + // make an expanded AACube centered on the object + const float PARENTED_EXPANSION_FACTOR = 3.0f; + float scale = PARENTED_EXPANSION_FACTOR * maxAACube.getScale(); + return AACube(maxAACube.calcCenter() - glm::vec3(0.5f * scale), scale); + } else { + return maxAACube; + } +} bool SpatiallyNestable::updateQueryAACube() { if (!queryAACubeNeedsUpdate()) { @@ -1100,20 +1121,12 @@ bool SpatiallyNestable::updateQueryAACube() { } bool success; - AACube maxAACube = getMaximumAACube(success); + AACube initialQueryAACube = calculateInitialQueryAACube(success); if (!success) { return false; } - - if (shouldPuffQueryAACube()) { - // make an expanded AACube centered on the object - float scale = PARENTED_EXPANSION_FACTOR * maxAACube.getScale(); - _queryAACube = AACube(maxAACube.calcCenter() - glm::vec3(0.5f * scale), scale); - _queryAACubeIsPuffed = true; - } else { - _queryAACube = maxAACube; - _queryAACubeIsPuffed = false; - } + _queryAACube = initialQueryAACube; + _queryAACubeIsPuffed = shouldPuffQueryAACube(); forEachDescendant([&](const SpatiallyNestablePointer& descendant) { bool childSuccess; @@ -1128,6 +1141,12 @@ bool SpatiallyNestable::updateQueryAACube() { }); _queryAACubeSet = true; + + auto parent = getParentPointer(success); + if (success && parent) { + parent->updateQueryAACube(); + } + return true; } @@ -1158,7 +1177,7 @@ bool SpatiallyNestable::queryAACubeNeedsUpdate() const { // make sure children are still in their boxes, also. bool childNeedsUpdate = false; forEachDescendantTest([&](const SpatiallyNestablePointer& descendant) { - if (!childNeedsUpdate && descendant->queryAACubeNeedsUpdate()) { + if (descendant->queryAACubeNeedsUpdate() || !_queryAACube.contains(descendant->getQueryAACube())) { childNeedsUpdate = true; // Don't recurse further return false; diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index a50e687a9b..03ed97afbd 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -19,7 +19,6 @@ #include "SpatialParentFinder.h" #include "shared/ReadWriteLockable.h" - class SpatiallyNestable; using SpatiallyNestableWeakPointer = std::weak_ptr; using SpatiallyNestableWeakConstPointer = std::weak_ptr; @@ -112,6 +111,7 @@ public: virtual glm::vec3 getParentAngularVelocity(bool& success) const; virtual AACube getMaximumAACube(bool& success) const; + virtual AACube calculateInitialQueryAACube(bool& success); virtual void setQueryAACube(const AACube& queryAACube); virtual bool queryAACubeNeedsUpdate() const; diff --git a/libraries/shared/src/StreamUtils.cpp b/libraries/shared/src/StreamUtils.cpp index 9ed0e24593..abacb012e5 100644 --- a/libraries/shared/src/StreamUtils.cpp +++ b/libraries/shared/src/StreamUtils.cpp @@ -78,6 +78,11 @@ QDebug& operator<<(QDebug& dbg, const glm::vec3& v) { return dbg; } +QDebug& operator<<(QDebug& dbg, const glm::u8vec3& v) { + dbg.nospace() << '(' << v.x << ", " << v.y << ", " << v.z << ')'; + return dbg; +} + QDebug& operator<<(QDebug& dbg, const glm::vec4& v) { dbg.nospace() << '(' << v.x << ", " << v.y << ", " << v.z << ", " << v.w << ')'; return dbg; diff --git a/libraries/shared/src/StreamUtils.h b/libraries/shared/src/StreamUtils.h index caf7b565f4..b6d6c522c5 100644 --- a/libraries/shared/src/StreamUtils.h +++ b/libraries/shared/src/StreamUtils.h @@ -42,6 +42,7 @@ class QDebug; // Add support for writing these to qDebug(). QDebug& operator<<(QDebug& s, const glm::vec2& v); QDebug& operator<<(QDebug& s, const glm::vec3& v); +QDebug& operator<<(QDebug& s, const glm::u8vec3& v); QDebug& operator<<(QDebug& s, const glm::vec4& v); QDebug& operator<<(QDebug& s, const glm::quat& q); QDebug& operator<<(QDebug& s, const glm::mat4& m); diff --git a/libraries/shared/src/shared/JSONHelpers.cpp b/libraries/shared/src/shared/JSONHelpers.cpp index c7cbf0e724..298a1ea85d 100644 --- a/libraries/shared/src/shared/JSONHelpers.cpp +++ b/libraries/shared/src/shared/JSONHelpers.cpp @@ -68,6 +68,20 @@ vec4 vec4FromJsonValue(const QJsonValue& v) { return glmFromJson(v); } +QJsonValue toJsonValueHelper(const QVariant& variant, int type) { + // User-registered types need explicit conversion + if (type == qMetaTypeId()) { + return toJsonValue(variant.value()); + } else if (type == qMetaTypeId()) { + return toJsonValue(variant.value()); + } else if (type == qMetaTypeId()) { + return toJsonValue(variant.value()); + } else { + // Qt types are converted automatically + return QJsonValue::fromVariant(variant); + } +} + QJsonValue toJsonValue(const QObject& o) { QJsonObject json{}; @@ -76,20 +90,8 @@ QJsonValue toJsonValue(const QObject& o) { for (int i = meta->propertyOffset(); i < meta->propertyCount(); ++i) { QString name = QString::fromLatin1(meta->property(i).name()); auto type = meta->property(i).userType(); - QVariant variant{ meta->property(i).read(&o) }; - QJsonValue value; - - // User-registered types need explicit conversion - if (type == qMetaTypeId()) { - value = toJsonValue(variant.value()); - } else if (type == qMetaTypeId()) { - value = toJsonValue(variant.value()); - } else if (type == qMetaTypeId()) { - value = toJsonValue(variant.value()); - } else { - // Qt types are converted automatically - value = QJsonValue::fromVariant(variant); - } + QVariant variant { meta->property(i).read(&o) }; + QJsonValue value = toJsonValueHelper(variant, type); json.insert(name, value); } @@ -106,6 +108,24 @@ QJsonValue toJsonValue(const QObject& o) { return json; } +QJsonValue toJsonValue(const QObject& o, const std::vector& props) { + QJsonObject json {}; + + const auto& meta = o.metaObject(); + // Only add the properties in props + for (auto& prop : props) { + int i = meta->indexOfProperty(prop.toStdString().c_str()); + QString name = QString::fromLatin1(meta->property(i).name()); + auto type = meta->property(i).userType(); + QVariant variant { meta->property(i).read(&o) }; + QJsonValue value = toJsonValueHelper(variant, type); + + json.insert(name, value); + } + + return json; +} + void qObjectFromJsonValue(const QJsonValue& j, QObject& o) { const QJsonObject object = j.toObject(); for (auto it = object.begin(); it != object.end(); it++) { diff --git a/libraries/shared/src/shared/JSONHelpers.h b/libraries/shared/src/shared/JSONHelpers.h index 735d33b5a5..a7e7077904 100644 --- a/libraries/shared/src/shared/JSONHelpers.h +++ b/libraries/shared/src/shared/JSONHelpers.h @@ -15,7 +15,9 @@ QJsonValue toJsonValue(const quat& q); QJsonValue toJsonValue(const vec3& v); QJsonValue toJsonValue(const vec4& v); +QJsonValue toJsonValueHelper(const QVariant& variant, int type); QJsonValue toJsonValue(const QObject& o); +QJsonValue toJsonValue(const QObject& o, const std::vector& props); quat quatFromJsonValue(const QJsonValue& q); vec3 vec3FromJsonValue(const QJsonValue& v); diff --git a/scripts/developer/tests/ambientSoundTest.js b/scripts/developer/tests/ambientSoundTest.js index d048d5f73d..98dbf8bba0 100644 --- a/scripts/developer/tests/ambientSoundTest.js +++ b/scripts/developer/tests/ambientSoundTest.js @@ -10,7 +10,7 @@ var uuid = Entities.addEntity({ maxVolume: 0.1, range: 25, disabled: true, - grabbableKey: { wantsTrigger: true }, + grab: { triggerable: true } }), lifetime: 600, }); diff --git a/scripts/developer/tests/controllerTableTest.js b/scripts/developer/tests/controllerTableTest.js index 239c7fd0ce..517ff0ab1a 100644 --- a/scripts/developer/tests/controllerTableTest.js +++ b/scripts/developer/tests/controllerTableTest.js @@ -1,7 +1,7 @@ "use strict"; /* jslint bitwise: true */ -/* global Script, Entities, MyAvatar, Vec3, Quat, Mat4 */ +/* global Script, Entities, MyAvatar, Vec3, Quat, Mat4, Overlays */ (function() { // BEGIN LOCAL_SCOPE @@ -19,7 +19,7 @@ var sectionCenterB = 0; var sectionCenterSign = 0; var yFlip = 0; - + var objects = []; var overlays = []; @@ -35,41 +35,41 @@ createPropsCube(index, false, false, true, true); createPropsModel(index, false, false, true, true); createSign(index, "Clone Dynamic Entity"); - }; + } function createCloneEntity(index) { createPropsCube(index, false, false, true, false); createPropsModel(index, false, false, true, false); createSign(index, "Clone Non-Dynamic Entity"); - }; + } function createNearGrabOverlay(index) { createPropsCubeOverlay(index, false, false, true, true); createPropsModelOverlay(index, false, false, true, true); createSign(index, "Near Grab Overlay"); - }; + } function createNearGrabEntity(index) { createPropsCube(index, false, false, false, false); createPropsModel(index, false, false, false, false); createSign(index, "Near Grab Entity"); - }; + } function createFarGrabEntity(index) { createPropsCube(index, true, false, false, false); createPropsModel(index, true, false, false, false); createSign(index, "Far Grab Entity"); - }; + } function createPropsModel(i, dynamic, collisionless, clone, cloneDynamic) { var propsModel = { name: "controller-tests model object " + i, type: "Model", modelURL: "http://headache.hungry.com/~seth/hifi/controller-tests/color-cube.obj", - + position: sectionCenterA, rotation: sectionRotation, - + gravity: (dynamic && !collisionless) ? { x: 0, y: -1, z: 0 } : { x: 0, y: 0, z: 0 }, dimensions: { x: 0.2, y: 0.2, z: 0.2 }, userData: JSON.stringify({ @@ -270,8 +270,8 @@ Entities.deleteEntity(nearbyID); } - for (var i = 0; i < overlays.length; i++) { - var overlayID = overlays[i]; + for (var j = 0; j < overlays.length; j++) { + var overlayID = overlays[j]; Overlays.deleteOverlay(overlayID); } }); diff --git a/scripts/system/avatarapp.js b/scripts/system/avatarapp.js index 10ccb66d96..ece35acce7 100644 --- a/scripts/system/avatarapp.js +++ b/scripts/system/avatarapp.js @@ -329,36 +329,18 @@ function isGrabbable(entityID) { return false; } - var properties = Entities.getEntityProperties(entityID, ['clientOnly', 'userData']); + var properties = Entities.getEntityProperties(entityID, ['clientOnly', 'grab.grabbable']); if (properties.clientOnly) { - var userData; - try { - userData = JSON.parse(properties.userData); - } catch (e) { - userData = {}; - } - - return userData.grabbableKey && userData.grabbableKey.grabbable; + return properties.grab.grabbable; } return false; } function setGrabbable(entityID, grabbable) { - var properties = Entities.getEntityProperties(entityID, ['clientOnly', 'userData']); + var properties = Entities.getEntityProperties(entityID, ['clientOnly']); if (properties.clientOnly) { - var userData; - try { - userData = JSON.parse(properties.userData); - } catch (e) { - userData = {}; - } - - if (userData.grabbableKey === undefined) { - userData.grabbableKey = {}; - } - userData.grabbableKey.grabbable = grabbable; - Entities.editEntity(entityID, {userData: JSON.stringify(userData)}); + Entities.editEntity(entityID, { grab: { grabbable: grabbable }}); } } diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index db622a6724..62db965444 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -10,9 +10,8 @@ /* global Script, Entities, Overlays, Controller, Vec3, Quat, getControllerWorldLocation, controllerDispatcherPlugins:true, controllerDispatcherPluginsNeedSort:true, LEFT_HAND, RIGHT_HAND, NEAR_GRAB_PICK_RADIUS, DEFAULT_SEARCH_SPHERE_DISTANCE, DISPATCHER_PROPERTIES, - getGrabPointSphereOffset, HMD, MyAvatar, Messages, findHandChildEntities, Picks, PickType, Pointers, COLORS_GRAB_SEARCHING_HALF_SQUEEZE - COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, TRIGGER_ON_VALUE, PointerManager, print getGrabPointSphereOffset, HMD, MyAvatar, Messages, findHandChildEntities, Picks, PickType, Pointers, + PointerManager, getGrabPointSphereOffset, HMD, MyAvatar, Messages, findHandChildEntities, Picks, PickType, Pointers, PointerManager, print, Selection, DISPATCHER_HOVERING_LIST, DISPATCHER_HOVERING_STYLE */ @@ -34,6 +33,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); var PROFILE = false; var DEBUG = false; + var SHOW_GRAB_SPHERE = false; + if (typeof Test !== "undefined") { PROFILE = true; @@ -51,6 +52,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); this.tabletID = null; this.blacklist = []; this.pointerManager = new PointerManager(); + this.grabSphereOverlays = [null, null]; // a module can occupy one or more "activity" slots while it's running. If all the required slots for a module are // not set to false (not in use), a module cannot start. When a module is using a slot, that module's name @@ -251,7 +253,28 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); for (h = LEFT_HAND; h <= RIGHT_HAND; h++) { if (controllerLocations[h].valid) { var controllerPosition = controllerLocations[h].position; - var nearbyEntityIDs = Entities.findEntities(controllerPosition, NEAR_MAX_RADIUS * sensorScaleFactor); + var findRadius = NEAR_MAX_RADIUS * sensorScaleFactor; + + if (SHOW_GRAB_SPHERE) { + if (this.grabSphereOverlays[h]) { + Overlays.editOverlay(this.grabSphereOverlays[h], { position: controllerLocations[h].position }); + } else { + var grabSphereSize = findRadius * 2; + this.grabSphereOverlays[h] = Overlays.addOverlay("sphere", { + position: controllerLocations[h].position, + dimensions: { x: grabSphereSize, y: grabSphereSize, z: grabSphereSize }, + color: { red: 30, green: 30, blue: 255 }, + alpha: 0.3, + solid: true, + visible: true, + // lineWidth: 2.0, + drawInFront: false, + grabbable: false + }); + } + } + + var nearbyEntityIDs = Entities.findEntities(controllerPosition, findRadius); for (var j = 0; j < nearbyEntityIDs.length; j++) { var entityID = nearbyEntityIDs[j]; var props = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES); diff --git a/scripts/system/controllers/controllerDisplay.js b/scripts/system/controllers/controllerDisplay.js index 8aa0393357..e40b761307 100644 --- a/scripts/system/controllers/controllerDisplay.js +++ b/scripts/system/controllers/controllerDisplay.js @@ -7,7 +7,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* globals createControllerDisplay:true deleteControllerDisplay:true */ +/* globals createControllerDisplay:true, deleteControllerDisplay:true, Controller, Overlays, Vec3, MyAvatar, Quat */ function clamp(value, min, max) { if (value < min) { @@ -188,7 +188,7 @@ createControllerDisplay = function(config) { for (var partName in controller.parts) { var part = controller.parts[partName]; var localPosition = Vec3.subtract(part.naturalPosition, controller.naturalPosition); - var localRotation = { x: 0, y: 0, z: 0, w: 1 } + var localRotation = { x: 0, y: 0, z: 0, w: 1 }; controllerDisplay.parts[partName] = controller.parts[partName]; @@ -203,7 +203,7 @@ createControllerDisplay = function(config) { if (part.defaultTextureLayer) { var textures = {}; textures[part.textureName] = part.textureLayers[part.defaultTextureLayer].defaultTextureURL; - properties['textures'] = textures; + properties.textures = textures; } var overlayID = Overlays.addOverlay("model", properties); diff --git a/scripts/system/controllers/controllerDisplayManager.js b/scripts/system/controllers/controllerDisplayManager.js index e3fd74e05b..f93f8b1624 100644 --- a/scripts/system/controllers/controllerDisplayManager.js +++ b/scripts/system/controllers/controllerDisplayManager.js @@ -7,8 +7,9 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* globals ControllerDisplayManager:true createControllerDisplay deleteControllerDisplay - VIVE_CONTROLLER_CONFIGURATION_LEFT VIVE_CONTROLLER_CONFIGURATION_RIGHT */ +/* globals ControllerDisplayManager:true, createControllerDisplay, deleteControllerDisplay, + VIVE_CONTROLLER_CONFIGURATION_LEFT, VIVE_CONTROLLER_CONFIGURATION_RIGHT, Script, HMD, Controller, + MyAvatar, Overlays, TOUCH_CONTROLLER_CONFIGURATION_LEFT, TOUCH_CONTROLLER_CONFIGURATION_RIGHT, Messages */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ (function () { @@ -93,7 +94,7 @@ ControllerDisplayManager = function() { if (controllerRight) { controllerRight.resize(sensorScaleFactor); } - }; + } var handleMessages = function(channel, message, sender) { var i, data, name, visible; diff --git a/scripts/system/controllers/controllerModules/disableOtherModule.js b/scripts/system/controllers/controllerModules/disableOtherModule.js index 0928b29d5d..7636c56f65 100644 --- a/scripts/system/controllers/controllerModules/disableOtherModule.js +++ b/scripts/system/controllers/controllerModules/disableOtherModule.js @@ -58,18 +58,16 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); if (channel === 'Hifi-Hand-Disabler') { if (message === 'left') { leftDisableModules.disableModules = true; - } - if (message === 'right') { + } else if (message === 'right') { rightDisableModules.disableModules = true; - } - if (message === 'both' || message === 'none') { - if (message === 'both') { - leftDisableModules.disableModules = true; - rightDisableModules.disableModules = true; - } else if (message === 'none') { - leftDisableModules.disableModules = false; - rightDisableModules.disableModules = false; - } + } else if (message === 'both') { + leftDisableModules.disableModules = true; + rightDisableModules.disableModules = true; + } else if (message === 'none') { + leftDisableModules.disableModules = false; + rightDisableModules.disableModules = false; + } else { + print("disableOtherModule -- unknown command: " + message); } } } diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index 91c8d89daf..f4d9c731b7 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -6,11 +6,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, Camera, +/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, Camera, print, getControllerJointIndex, enableDispatcherModule, disableDispatcherModule, entityIsFarGrabbedByOther, Messages, makeDispatcherModuleParameters, makeRunningValues, Settings, entityHasActions, Vec3, Overlays, flatten, Xform, getControllerWorldLocation, ensureDynamic, entityIsCloneable, - cloneEntity, DISPATCHER_PROPERTIES, Uuid, unhighlightTargetEntity, isInEditMode + cloneEntity, DISPATCHER_PROPERTIES, Uuid, unhighlightTargetEntity, isInEditMode, getGrabbableData */ Script.include("/~/system/libraries/Xform.js"); @@ -66,12 +66,16 @@ EquipHotspotBuddy.prototype.updateHotspot = function(hotspot, timestamp) { overlays: [] }; - var diameter = hotspot.radius * 2; + var dimensions = hotspot.radius * 2 * EQUIP_SPHERE_SCALE_FACTOR; + + if (hotspot.indicatorURL) { + dimensions = hotspot.indicatorScale; + } // override default sphere with a user specified model, if it exists. overlayInfoSet.overlays.push(Overlays.addOverlay("model", { name: "hotspot overlay", - url: hotspot.modelURL ? hotspot.modelURL : DEFAULT_SPHERE_MODEL_URL, + url: hotspot.indicatorURL ? hotspot.indicatorURL : DEFAULT_SPHERE_MODEL_URL, position: hotspot.worldPosition, rotation: { x: 0, @@ -79,8 +83,7 @@ EquipHotspotBuddy.prototype.updateHotspot = function(hotspot, timestamp) { z: 0, w: 1 }, - dimensions: diameter * EQUIP_SPHERE_SCALE_FACTOR, - scale: hotspot.modelScale, + dimensions: dimensions, ignoreRayIntersection: true })); overlayInfoSet.type = "model"; @@ -137,8 +140,13 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var position = entityXform.xformPoint(overlayInfoSet.localPosition); var dimensions; - if (overlayInfoSet.type === "sphere") { - dimensions = (overlayInfoSet.hotspot.radius / 2) * overlayInfoSet.currentSize * EQUIP_SPHERE_SCALE_FACTOR; + if (overlayInfoSet.hotspot.indicatorURL) { + var ratio = overlayInfoSet.currentSize / overlayInfoSet.targetSize; + dimensions = { + x: overlayInfoSet.hotspot.dimensions.x * ratio, + y: overlayInfoSet.hotspot.dimensions.y * ratio, + z: overlayInfoSet.hotspot.dimensions.z * ratio + }; } else { dimensions = (overlayInfoSet.hotspot.radius / 2) * overlayInfoSet.currentSize; } @@ -158,12 +166,21 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa } }; + +var alreadyWarned = {}; +function warnAboutUserData(props) { + if (alreadyWarned[props.id]) { + return; + } + print("Warning -- overriding grab properties with userData for " + props.id + " / " + props.name); + alreadyWarned[props.id] = true; +} + + (function() { var ATTACH_POINT_SETTINGS = "io.highfidelity.attachPoints"; - var EQUIP_RADIUS = 1.0; // radius used for palm vs equip-hotspot for equipping. - var HAPTIC_PULSE_STRENGTH = 1.0; var HAPTIC_PULSE_DURATION = 13.0; var HAPTIC_TEXTURE_STRENGTH = 0.1; @@ -176,36 +193,86 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var TRIGGER_OFF_VALUE = 0.1; var TRIGGER_ON_VALUE = TRIGGER_OFF_VALUE + 0.05; // Squeezed just enough to activate search or near grab var BUMPER_ON_VALUE = 0.5; - + var EMPTY_PARENT_ID = "{00000000-0000-0000-0000-000000000000}"; - + var UNEQUIP_KEY = "u"; function getWearableData(props) { - var wearable = {}; - try { - if (!props.userDataParsed) { - props.userDataParsed = JSON.parse(props.userData); - } + if (props.grab.equippable) { + // if equippable is true, we know this was already converted from the old userData style to properties + return { + joints: { + LeftHand: [ props.grab.equippableLeftPosition, props.grab.equippableLeftRotation ], + RightHand: [ props.grab.equippableRightPosition, props.grab.equippableRightRotation ] + }, + indicatorURL: props.grab.equippableIndicatorURL, + indicatorScale: props.grab.equippableIndicatorScale, + indicatorOffset: props.grab.equippableIndicatorOffset + }; + } else { + // check for old userData equippability. The JSON reader will convert userData to properties + // in EntityTree.cpp, but this won't catch things created from scripts or some items in + // the market. Eventually we'll remove this section. + try { + if (!props.userDataParsed) { + props.userDataParsed = JSON.parse(props.userData); + } + var userDataParsed = props.userDataParsed; - wearable = props.userDataParsed.wearable ? props.userDataParsed.wearable : {}; - } catch (err) { - // don't want to spam the logs - } - return wearable; - } - function getEquipHotspotsData(props) { - var equipHotspots = []; - try { - if (!props.userDataParsed) { - props.userDataParsed = JSON.parse(props.userData); - } + // userData: { wearable: { joints: { LeftHand: {...}, RightHand: {...} } } } + if (userDataParsed.wearable && userDataParsed.wearable.joints) { + warnAboutUserData(props); + userDataParsed.wearable.indicatorURL = ""; + userDataParsed.wearable.indicatorScale = { x: 1, y: 1, z: 1 }; + userDataParsed.wearable.indicatorOffset = { x: 0, y: 0, z: 0 }; + return userDataParsed.wearable; + } - equipHotspots = props.userDataParsed.equipHotspots ? props.userDataParsed.equipHotspots : []; - } catch (err) { - // don't want to spam the logs + // userData: { equipHotspots: { joints: { LeftHand: {...}, RightHand: {...} } } } + // https://highfidelity.atlassian.net/wiki/spaces/HOME/pages/51085337/Authoring+Equippable+Entities + if (userDataParsed.equipHotspots && + userDataParsed.equipHotspots.length > 0 && + userDataParsed.equipHotspots[0].joints) { + warnAboutUserData(props); + var hotSpot = userDataParsed.equipHotspots[0]; + + var indicatorScale = { x: hotSpot.radius, y: hotSpot.radius, z: hotSpot.radius }; + if (hotSpot.modelURL && hotSpot.modelURL !== "") { + indicatorScale = hotSpot.modelScale; + } + + return { + joints: hotSpot.joints, + indicatorURL: hotSpot.modelURL, + indicatorScale: indicatorScale, + indicatorOffset: hotSpot.position, + }; + } + + // userData:{grabbableKey:{spatialKey:{leftRelativePosition:{...},rightRelativePosition:{...}}}} + if (userDataParsed.grabbableKey && + userDataParsed.grabbableKey.spatialKey) { + warnAboutUserData(props); + var joints = {}; + joints.LeftHand = [ { x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 0, w: 1 } ]; + joints.RightHand = [ { x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 0, w: 1 } ]; + if (userDataParsed.grabbableKey.spatialKey.leftRelativePosition) { + joints.LeftHand = [userDataParsed.grabbableKey.spatialKey.leftRelativePosition, + userDataParsed.grabbableKey.spatialKey.relativeRotation]; + } + if (userDataParsed.grabbableKey.spatialKey.rightRelativePosition) { + joints.RightHand = [userDataParsed.grabbableKey.spatialKey.rightRelativePosition, + userDataParsed.grabbableKey.spatialKey.relativeRotation]; + } + return { joints: joints }; + } + + } catch (err) { + // don't spam logs + } + return null; } - return equipHotspots; } function getAttachPointSettings() { @@ -275,7 +342,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa this.handHasBeenRightsideUp = false; this.parameters = makeDispatcherModuleParameters( - 300, + 115, this.hand === RIGHT_HAND ? ["rightHand", "rightHandEquip"] : ["leftHand", "leftHandEquip"], [], 100); @@ -299,51 +366,29 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa // * radius {number} radius of equip hotspot // * joints {Object} keys are joint names values are arrays of two elements: // offset position {Vec3} and offset rotation {Quat}, both are in the coordinate system of the joint. - // * modelURL {string} url for model to use instead of default sphere. - // * modelScale {Vec3} scale factor for model + // * indicatorURL {string} url for model to use instead of default sphere. + // * indicatorScale {Vec3} scale factor for model this.collectEquipHotspots = function(props) { var result = []; var entityID = props.id; var entityXform = new Xform(props.rotation, props.position); - var equipHotspotsProps = getEquipHotspotsData(props); - if (equipHotspotsProps && equipHotspotsProps.length > 0) { - var i, length = equipHotspotsProps.length; - for (i = 0; i < length; i++) { - var hotspot = equipHotspotsProps[i]; - if (hotspot.position && hotspot.radius && hotspot.joints) { - result.push({ - key: entityID.toString() + i.toString(), - entityID: entityID, - localPosition: hotspot.position, - worldPosition: entityXform.xformPoint(hotspot.position), - radius: hotspot.radius, - joints: hotspot.joints, - modelURL: hotspot.modelURL, - modelScale: hotspot.modelScale - }); - } - } - } else { - var wearableProps = getWearableData(props); - var sensorToScaleFactor = MyAvatar.sensorToWorldScale; - if (wearableProps && wearableProps.joints) { - - result.push({ - key: entityID.toString() + "0", - entityID: entityID, - localPosition: { - x: 0, - y: 0, - z: 0 - }, - worldPosition: entityXform.pos, - radius: EQUIP_RADIUS * sensorToScaleFactor, - joints: wearableProps.joints, - modelURL: null, - modelScale: null - }); - } + var wearableProps = getWearableData(props); + var sensorToScaleFactor = MyAvatar.sensorToWorldScale; + if (wearableProps && wearableProps.joints) { + result.push({ + key: entityID.toString() + "0", + entityID: entityID, + localPosition: wearableProps.indicatorOffset, + worldPosition: entityXform.pos, + radius: ((wearableProps.indicatorScale.x + + wearableProps.indicatorScale.y + + wearableProps.indicatorScale.z) / 3) * sensorToScaleFactor, + dimensions: wearableProps.indicatorScale, + joints: wearableProps.joints, + indicatorURL: wearableProps.indicatorURL, + indicatorScale: wearableProps.indicatorScale, + }); } return result; }; @@ -492,7 +537,8 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa }; Messages.sendLocalMessage('Hifi-unhighlight-entity', JSON.stringify(message)); - var grabbedProperties = Entities.getEntityProperties(this.targetEntityID); + var grabbedProperties = Entities.getEntityProperties(this.targetEntityID, DISPATCHER_PROPERTIES); + var grabData = getGrabbableData(grabbedProperties); // if an object is "equipped" and has a predefined offset, use it. if (this.grabbedHotspot) { @@ -510,7 +556,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa } var handJointIndex; - if (this.ignoreIK) { + if (grabData.grabFollowsController) { handJointIndex = this.controllerJointIndex; } else { handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); @@ -796,7 +842,8 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa if (intersection.intersects) { var entityID = intersection.entityID; var entityProperties = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES); - var hasEquipData = getWearableData(entityProperties).joints || getEquipHotspotsData(entityProperties).length > 0; + entityProperties.id = entityID; + var hasEquipData = getWearableData(entityProperties); if (hasEquipData && entityProperties.parentID === EMPTY_PARENT_ID && !entityIsFarGrabbedByOther(entityID)) { entityProperties.id = entityID; var rightHandPosition = MyAvatar.getJointPosition("RightHand"); @@ -804,7 +851,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var distanceToRightHand = Vec3.distance(entityProperties.position, rightHandPosition); var distanceToLeftHand = Vec3.distance(entityProperties.position, leftHandPosition); var leftHandAvailable = leftEquipEntity.targetEntityID === null; - var rightHandAvailable = rightEquipEntity.targetEntityID === null; + var rightHandAvailable = rightEquipEntity.targetEntityID === null; if (rightHandAvailable && (distanceToRightHand < distanceToLeftHand || !leftHandAvailable)) { // clear any existing grab actions on the entity now (their later removal could affect bootstrapping flags) clearGrabActions(entityID); @@ -817,7 +864,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa } } }; - + var onKeyPress = function(event) { if (event.text.toLowerCase() === UNEQUIP_KEY) { if (rightEquipEntity.targetEntityID) { @@ -828,7 +875,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa } } }; - + var deleteEntity = function(entityID) { if (rightEquipEntity.targetEntityID === entityID) { rightEquipEntity.endEquipEntity(); @@ -837,7 +884,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa leftEquipEntity.endEquipEntity(); } }; - + var clearEntities = function() { if (rightEquipEntity.targetEntityID) { rightEquipEntity.endEquipEntity(); @@ -846,7 +893,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa leftEquipEntity.endEquipEntity(); } }; - + Messages.subscribe('Hifi-Hand-Grab'); Messages.subscribe('Hifi-Hand-Drop'); Messages.messageReceived.connect(handleMessage); diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 2e73526728..4b5b11bdb5 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -8,38 +8,19 @@ /* jslint bitwise: true */ /* global Script, Controller, RIGHT_HAND, LEFT_HAND, Mat4, MyAvatar, Vec3, Camera, Quat, - getGrabPointSphereOffset, getEnabledModuleByName, makeRunningValues, Entities, + getEnabledModuleByName, makeRunningValues, Entities, enableDispatcherModule, disableDispatcherModule, entityIsDistanceGrabbable, entityIsGrabbable, makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, - PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, - DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic, - getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays, isPointingAtUI - Picks, makeLaserLockInfo Xform, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST, - worldPositionToRegistrationFrameMatrix + TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic, + getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, + Picks, makeLaserLockInfo, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST, + worldPositionToRegistrationFrameMatrix, DISPATCHER_PROPERTIES, Uuid, Picks */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); -Script.include("/~/system/libraries/Xform.js"); (function() { - var GRABBABLE_PROPERTIES = [ - "position", - "registrationPoint", - "rotation", - "gravity", - "collidesWith", - "dynamic", - "collisionless", - "locked", - "name", - "shapeType", - "parentID", - "parentJointIndex", - "density", - "dimensions", - "userData" - ]; var MARGIN = 25; @@ -106,9 +87,9 @@ Script.include("/~/system/libraries/Xform.js"); this.locked = false; this.highlightedEntity = null; this.reticleMinX = MARGIN; - this.reticleMaxX; + this.reticleMaxX = null; this.reticleMinY = MARGIN; - this.reticleMaxY; + this.reticleMaxY = null; this.ignoredEntities = []; @@ -212,7 +193,7 @@ Script.include("/~/system/libraries/Xform.js"); var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix()); var roomControllerPosition = Mat4.transformPoint(worldToSensorMat, worldControllerPosition); - var grabbedProperties = Entities.getEntityProperties(this.grabbedThingID, GRABBABLE_PROPERTIES); + var grabbedProperties = Entities.getEntityProperties(this.grabbedThingID, DISPATCHER_PROPERTIES); var now = Date.now(); var deltaObjectTime = (now - this.currentObjectTime) / MSECS_PER_SEC; // convert to seconds this.currentObjectTime = now; @@ -329,7 +310,7 @@ Script.include("/~/system/libraries/Xform.js"); this.notPointingAtEntity = function(controllerData) { var intersection = controllerData.rayPicks[this.hand]; - var entityProperty = Entities.getEntityProperties(intersection.objectID); + var entityProperty = Entities.getEntityProperties(intersection.objectID, DISPATCHER_PROPERTIES); var entityType = entityProperty.type; var hudRayPick = controllerData.hudRayPicks[this.hand]; var point2d = this.calculateNewReticlePosition(hudRayPick.intersection); @@ -373,7 +354,7 @@ Script.include("/~/system/libraries/Xform.js"); var worldControllerPosition = controllerLocation.position; var worldControllerRotation = controllerLocation.orientation; - var grabbedProperties = Entities.getEntityProperties(intersection.objectID, GRABBABLE_PROPERTIES); + var grabbedProperties = Entities.getEntityProperties(intersection.objectID, DISPATCHER_PROPERTIES); this.currentObjectPosition = grabbedProperties.position; this.grabRadius = intersection.distance; @@ -395,7 +376,7 @@ Script.include("/~/system/libraries/Xform.js"); }; this.targetIsNull = function() { - var properties = Entities.getEntityProperties(this.grabbedThingID); + var properties = Entities.getEntityProperties(this.grabbedThingID, DISPATCHER_PROPERTIES); if (Object.keys(properties).length === 0 && this.distanceHolding) { return true; } @@ -460,8 +441,8 @@ Script.include("/~/system/libraries/Xform.js"); // if we are doing a distance grab and the object or tablet gets close enough to the controller, // stop the far-grab so the near-grab or equip can take over. for (var k = 0; k < nearGrabReadiness.length; k++) { - if (nearGrabReadiness[k].active && (nearGrabReadiness[k].targets[0] === this.grabbedThingID - || HMD.tabletID && nearGrabReadiness[k].targets[0] === HMD.tabletID)) { + if (nearGrabReadiness[k].active && (nearGrabReadiness[k].targets[0] === this.grabbedThingID || + HMD.tabletID && nearGrabReadiness[k].targets[0] === HMD.tabletID)) { this.endFarGrabAction(); this.restoreIgnoredEntities(); return makeRunningValues(false, [], []); @@ -487,11 +468,7 @@ Script.include("/~/system/libraries/Xform.js"); Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity); this.highlightedEntity = null; - var targetProps = Entities.getEntityProperties(entityID, [ - "dynamic", "shapeType", "position", - "rotation", "dimensions", "density", - "userData", "locked", "type", "href" - ]); + var targetProps = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES); if (targetProps.href !== "") { AddressManager.handleLookupString(targetProps.href); this.restoreIgnoredEntities(); @@ -540,11 +517,7 @@ Script.include("/~/system/libraries/Xform.js"); if (this.highlightedEntity !== targetEntityID) { Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity); - var selectionTargetProps = Entities.getEntityProperties(targetEntityID, [ - "dynamic", "shapeType", "position", - "rotation", "dimensions", "density", - "userData", "locked", "type", "href" - ]); + var selectionTargetProps = Entities.getEntityProperties(targetEntityID, DISPATCHER_PROPERTIES); var selectionTargetObject = new TargetObject(targetEntityID, selectionTargetProps); selectionTargetObject.parentProps = getEntityParents(selectionTargetProps); @@ -574,11 +547,12 @@ Script.include("/~/system/libraries/Xform.js"); if (!_this.entityWithContextOverlay && _this.contextOverlayTimer && _this.potentialEntityWithContextOverlay === rayPickInfo.objectID) { - var props = Entities.getEntityProperties(rayPickInfo.objectID); + var props = Entities.getEntityProperties(rayPickInfo.objectID, DISPATCHER_PROPERTIES); var pointerEvent = { type: "Move", id: _this.hand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(rayPickInfo.objectID, rayPickInfo.intersection, props), + pos2D: projectOntoEntityXYPlane(rayPickInfo.objectID, + rayPickInfo.intersection, props), pos3D: rayPickInfo.intersection, normal: rayPickInfo.surfaceNormal, direction: Vec3.subtract(ZERO_VEC, rayPickInfo.surfaceNormal), diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js b/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js index 78abcb9b20..571b8feda3 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js @@ -11,32 +11,14 @@ makeRunningValues, Entities, enableDispatcherModule, disableDispatcherModule, entityIsGrabbable, makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, - Picks, makeLaserLockInfo, Xform, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST, - Uuid, worldPositionToRegistrationFrameMatrix + Picks, makeLaserLockInfo, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST, + Uuid, worldPositionToRegistrationFrameMatrix, DISPATCHER_PROPERTIES */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); -Script.include("/~/system/libraries/Xform.js"); (function() { - var GRABBABLE_PROPERTIES = [ - "position", - "registrationPoint", - "rotation", - "gravity", - "collidesWith", - "dynamic", - "collisionless", - "locked", - "name", - "shapeType", - "parentID", - "parentJointIndex", - "density", - "dimensions", - "userData" - ]; var MARGIN = 25; @@ -45,7 +27,6 @@ Script.include("/~/system/libraries/Xform.js"); this.entityProps = entityProps; this.targetEntityID = null; this.targetEntityProps = null; - this.previousCollisionStatus = null; this.getTargetEntity = function() { var parentPropsLength = this.parentProps.length; @@ -74,7 +55,6 @@ Script.include("/~/system/libraries/Xform.js"); this.potentialEntityWithContextOverlay = false; this.entityWithContextOverlay = false; this.contextOverlayTimer = false; - this.previousCollisionStatus = false; this.locked = false; this.highlightedEntity = null; this.reticleMinX = MARGIN; @@ -182,7 +162,7 @@ Script.include("/~/system/libraries/Xform.js"); var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix()); var roomControllerPosition = Mat4.transformPoint(worldToSensorMat, worldControllerPosition); - var grabbedProperties = Entities.getEntityProperties(this.grabbedThingID, GRABBABLE_PROPERTIES); + var grabbedProperties = Entities.getEntityProperties(this.grabbedThingID, DISPATCHER_PROPERTIES); var now = Date.now(); var deltaObjectTime = (now - this.currentObjectTime) / MSECS_PER_SEC; // convert to seconds this.currentObjectTime = now; @@ -284,7 +264,7 @@ Script.include("/~/system/libraries/Xform.js"); this.notPointingAtEntity = function(controllerData) { var intersection = controllerData.rayPicks[this.hand]; - var entityProperty = Entities.getEntityProperties(intersection.objectID); + var entityProperty = Entities.getEntityProperties(intersection.objectID, DISPATCHER_PROPERTIES); var entityType = entityProperty.type; var hudRayPick = controllerData.hudRayPicks[this.hand]; var point2d = this.calculateNewReticlePosition(hudRayPick.intersection); @@ -319,7 +299,7 @@ Script.include("/~/system/libraries/Xform.js"); var worldControllerPosition = controllerLocation.position; var worldControllerRotation = controllerLocation.orientation; - var grabbedProperties = Entities.getEntityProperties(intersection.objectID, GRABBABLE_PROPERTIES); + var grabbedProperties = Entities.getEntityProperties(intersection.objectID, DISPATCHER_PROPERTIES); this.currentObjectPosition = grabbedProperties.position; this.grabRadius = intersection.distance; @@ -341,7 +321,7 @@ Script.include("/~/system/libraries/Xform.js"); }; this.targetIsNull = function() { - var properties = Entities.getEntityProperties(this.grabbedThingID); + var properties = Entities.getEntityProperties(this.grabbedThingID, DISPATCHER_PROPERTIES); if (Object.keys(properties).length === 0 && this.distanceHolding) { return true; } @@ -351,7 +331,7 @@ Script.include("/~/system/libraries/Xform.js"); this.getTargetProps = function (controllerData) { var targetEntityID = controllerData.rayPicks[this.hand].objectID; if (targetEntityID) { - return Entities.getEntityProperties(targetEntityID); + return Entities.getEntityProperties(targetEntityID, DISPATCHER_PROPERTIES); } return null; }; @@ -435,11 +415,7 @@ Script.include("/~/system/libraries/Xform.js"); Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity); this.highlightedEntity = null; - var targetProps = Entities.getEntityProperties(entityID, [ - "dynamic", "shapeType", "position", - "rotation", "dimensions", "density", - "userData", "locked", "type", "href" - ]); + var targetProps = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES); if (targetProps.href !== "") { AddressManager.handleLookupString(targetProps.href); return makeRunningValues(false, [], []); @@ -488,11 +464,7 @@ Script.include("/~/system/libraries/Xform.js"); if (this.highlightedEntity !== targetEntityID) { Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity); - var selectionTargetProps = Entities.getEntityProperties(targetEntityID, [ - "dynamic", "shapeType", "position", - "rotation", "dimensions", "density", - "userData", "locked", "type", "href" - ]); + var selectionTargetProps = Entities.getEntityProperties(targetEntityID, DISPATCHER_PROPERTIES); var selectionTargetObject = new TargetObject(targetEntityID, selectionTargetProps); selectionTargetObject.parentProps = getEntityParents(selectionTargetProps); @@ -522,7 +494,8 @@ Script.include("/~/system/libraries/Xform.js"); if (!_this.entityWithContextOverlay && _this.contextOverlayTimer && _this.potentialEntityWithContextOverlay === rayPickInfo.objectID) { - var pEvProps = Entities.getEntityProperties(rayPickInfo.objectID); + var pEvProps = Entities.getEntityProperties(rayPickInfo.objectID, + DISPATCHER_PROPERTIES); var pointerEvent = { type: "Move", id: _this.hand + 1, // 0 is reserved for hardware mouse diff --git a/scripts/system/controllers/controllerModules/farParentGrabEntity.js b/scripts/system/controllers/controllerModules/farParentGrabEntity.js index a9ec246a32..45fe606c0f 100644 --- a/scripts/system/controllers/controllerModules/farParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farParentGrabEntity.js @@ -10,34 +10,15 @@ /* global Script, Controller, RIGHT_HAND, LEFT_HAND, Mat4, MyAvatar, Vec3, Quat, getEnabledModuleByName, makeRunningValues, Entities, enableDispatcherModule, disableDispatcherModule, entityIsGrabbable, makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, getControllerWorldLocation, - projectOntoEntityXYPlane, ContextOverlay, HMD, Picks, makeLaserLockInfo, Xform, makeLaserParams, AddressManager, + projectOntoEntityXYPlane, ContextOverlay, HMD, Picks, makeLaserLockInfo, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST, unhighlightTargetEntity, Messages, Uuid, findGroupParent, - worldPositionToRegistrationFrameMatrix + worldPositionToRegistrationFrameMatrix, DISPATCHER_PROPERTIES, findFarGrabJointChildEntities */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); -Script.include("/~/system/libraries/Xform.js"); (function() { - var GRABBABLE_PROPERTIES = [ - "position", - "registrationPoint", - "rotation", - "gravity", - "collidesWith", - "dynamic", - "collisionless", - "locked", - "name", - "shapeType", - "parentID", - "parentJointIndex", - "density", - "dimensions", - "userData" - ]; - var MARGIN = 25; function TargetObject(entityID, entityProps) { @@ -68,6 +49,7 @@ Script.include("/~/system/libraries/Xform.js"); this.hand = hand; this.targetEntityID = null; this.targetObject = null; + this.previouslyUnhooked = {}; this.previousParentID = {}; this.previousParentJointIndex = {}; this.potentialEntityWithContextOverlay = false; @@ -78,6 +60,7 @@ Script.include("/~/system/libraries/Xform.js"); this.reticleMaxX = 0; this.reticleMinY = MARGIN; this.reticleMaxY = 0; + this.lastUnexpectedChildrenCheckTime = 0; var FAR_GRAB_JOINTS = [65527, 65528]; // FARGRAB_LEFTHAND_INDEX, FARGRAB_RIGHTHAND_INDEX @@ -214,7 +197,7 @@ Script.include("/~/system/libraries/Xform.js"); var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix()); var roomControllerPosition = Mat4.transformPoint(worldToSensorMat, worldControllerPosition); - var grabbedProperties = Entities.getEntityProperties(this.targetEntityID, GRABBABLE_PROPERTIES); + var grabbedProperties = Entities.getEntityProperties(this.targetEntityID, DISPATCHER_PROPERTIES); var now = Date.now(); var deltaObjectTime = (now - this.currentObjectTime) / MSECS_PER_SEC; // convert to seconds this.currentObjectTime = now; @@ -275,7 +258,7 @@ Script.include("/~/system/libraries/Xform.js"); this.endFarParentGrab = function (controllerData) { this.hapticTargetID = null; // var endProps = controllerData.nearbyEntityPropertiesByID[this.targetEntityID]; - var endProps = Entities.getEntityProperties(this.targetEntityID, GRABBABLE_PROPERTIES); + var endProps = Entities.getEntityProperties(this.targetEntityID, DISPATCHER_PROPERTIES); if (this.thisFarGrabJointIsParent(endProps)) { Entities.editEntity(this.targetEntityID, { parentID: this.previousParentID[this.targetEntityID], @@ -313,7 +296,7 @@ Script.include("/~/system/libraries/Xform.js"); this.notPointingAtEntity = function(controllerData) { var intersection = controllerData.rayPicks[this.hand]; - var entityProperty = Entities.getEntityProperties(intersection.objectID); + var entityProperty = Entities.getEntityProperties(intersection.objectID, DISPATCHER_PROPERTIES); var entityType = entityProperty.type; var hudRayPick = controllerData.hudRayPicks[this.hand]; var point2d = this.calculateNewReticlePosition(hudRayPick.intersection); @@ -348,7 +331,7 @@ Script.include("/~/system/libraries/Xform.js"); var worldControllerPosition = controllerLocation.position; var worldControllerRotation = controllerLocation.orientation; - var grabbedProperties = Entities.getEntityProperties(intersection.objectID, GRABBABLE_PROPERTIES); + var grabbedProperties = Entities.getEntityProperties(intersection.objectID, DISPATCHER_PROPERTIES); this.currentObjectPosition = grabbedProperties.position; this.grabRadius = intersection.distance; @@ -369,8 +352,51 @@ Script.include("/~/system/libraries/Xform.js"); } }; + this.checkForUnexpectedChildren = function (controllerData) { + // sometimes things can get parented to a hand and this script is unaware. Search for such entities and + // unhook them. + + var now = Date.now(); + var UNEXPECTED_CHILDREN_CHECK_TIME = 0.1; // seconds + if (now - this.lastUnexpectedChildrenCheckTime > MSECS_PER_SEC * UNEXPECTED_CHILDREN_CHECK_TIME) { + this.lastUnexpectedChildrenCheckTime = now; + + var children = findFarGrabJointChildEntities(this.hand); + var _this = this; + + children.forEach(function(childID) { + // we appear to be holding something and this script isn't in a state that would be holding something. + // unhook it. if we previously took note of this entity's parent, put it back where it was. This + // works around some problems that happen when more than one hand or avatar is passing something around. + if (_this.previousParentID[childID]) { + var previousParentID = _this.previousParentID[childID]; + var previousParentJointIndex = _this.previousParentJointIndex[childID]; + + // The main flaw with keeping track of previous parentage in individual scripts is: + // (1) A grabs something (2) B takes it from A (3) A takes it from B (4) A releases it + // now A and B will take turns passing it back to the other. Detect this and stop the loop here... + var UNHOOK_LOOP_DETECT_MS = 200; + if (_this.previouslyUnhooked[childID]) { + if (now - _this.previouslyUnhooked[childID] < UNHOOK_LOOP_DETECT_MS) { + previousParentID = Uuid.NULL; + previousParentJointIndex = -1; + } + } + _this.previouslyUnhooked[childID] = now; + + Entities.editEntity(childID, { + parentID: previousParentID, + parentJointIndex: previousParentJointIndex + }); + } else { + Entities.editEntity(childID, { parentID: Uuid.NULL }); + } + }); + } + }; + this.targetIsNull = function() { - var properties = Entities.getEntityProperties(this.targetEntityID, GRABBABLE_PROPERTIES); + var properties = Entities.getEntityProperties(this.targetEntityID, DISPATCHER_PROPERTIES); if (Object.keys(properties).length === 0 && this.distanceHolding) { return true; } @@ -380,7 +406,7 @@ Script.include("/~/system/libraries/Xform.js"); this.getTargetProps = function (controllerData) { var targetEntity = controllerData.rayPicks[this.hand].objectID; if (targetEntity) { - var gtProps = Entities.getEntityProperties(targetEntity, GRABBABLE_PROPERTIES); + var gtProps = Entities.getEntityProperties(targetEntity, DISPATCHER_PROPERTIES); if (entityIsGrabbable(gtProps)) { // give haptic feedback if (gtProps.id !== this.hapticTargetID) { @@ -416,6 +442,7 @@ Script.include("/~/system/libraries/Xform.js"); return makeRunningValues(true, [], []); } } else { + this.checkForUnexpectedChildren(controllerData); this.destroyContextOverlay(); return makeRunningValues(false, [], []); } @@ -480,11 +507,7 @@ Script.include("/~/system/libraries/Xform.js"); var entityID = rayPickInfo.objectID; Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity); this.highlightedEntity = null; - var targetProps = Entities.getEntityProperties(entityID, [ - "dynamic", "shapeType", "position", - "rotation", "dimensions", "density", - "userData", "locked", "type", "href" - ]); + var targetProps = Entities.getEntityProperties(entityID, DISPATCHER_PROPERTIES); if (targetProps.href !== "") { AddressManager.handleLookupString(targetProps.href); return makeRunningValues(false, [], []); @@ -533,11 +556,7 @@ Script.include("/~/system/libraries/Xform.js"); var targetEntityID = rayPickInfo.objectID; if (this.highlightedEntity !== targetEntityID) { Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity); - var selectionTargetProps = Entities.getEntityProperties(targetEntityID, [ - "dynamic", "shapeType", "position", - "rotation", "dimensions", "density", - "userData", "locked", "type", "href" - ]); + var selectionTargetProps = Entities.getEntityProperties(targetEntityID, DISPATCHER_PROPERTIES); var selectionTargetObject = new TargetObject(targetEntityID, selectionTargetProps); selectionTargetObject.parentProps = getEntityParents(selectionTargetProps); @@ -567,7 +586,8 @@ Script.include("/~/system/libraries/Xform.js"); if (!_this.entityWithContextOverlay && _this.contextOverlayTimer && _this.potentialEntityWithContextOverlay === rayPickInfo.objectID) { - var cotProps = Entities.getEntityProperties(rayPickInfo.objectID); + var cotProps = Entities.getEntityProperties(rayPickInfo.objectID, + DISPATCHER_PROPERTIES); var pointerEvent = { type: "Move", id: _this.hand + 1, // 0 is reserved for hardware mouse diff --git a/scripts/system/controllers/controllerModules/farTrigger.js b/scripts/system/controllers/controllerModules/farTrigger.js index 25df17ee84..2b003e4732 100644 --- a/scripts/system/controllers/controllerModules/farTrigger.js +++ b/scripts/system/controllers/controllerModules/farTrigger.js @@ -6,19 +6,18 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, Controller, RIGHT_HAND, LEFT_HAND, MyAvatar, getGrabPointSphereOffset, +/* global Script, RIGHT_HAND, LEFT_HAND, MyAvatar, makeRunningValues, Entities, enableDispatcherModule, disableDispatcherModule, makeDispatcherModuleParameters, - PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, - DEFAULT_SEARCH_SPHERE_DISTANCE, getGrabbableData, makeLaserParams + getGrabbableData, makeLaserParams, DISPATCHER_PROPERTIES */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); (function() { - function entityWantsNearTrigger(props) { + function entityWantsFarTrigger(props) { var grabbableData = getGrabbableData(props); - return grabbableData.triggerable || grabbableData.wantsTrigger; + return grabbableData.triggerable; } function FarTriggerEntity(hand) { @@ -37,11 +36,10 @@ Script.include("/~/system/libraries/controllers.js"); makeLaserParams(this.hand, false)); this.getTargetProps = function (controllerData) { - // nearbyEntityProperties is already sorted by length from controller var targetEntity = controllerData.rayPicks[this.hand].objectID; if (targetEntity) { - var targetProperties = Entities.getEntityProperties(targetEntity); - if (entityWantsNearTrigger(targetProperties)) { + var targetProperties = Entities.getEntityProperties(targetEntity, DISPATCHER_PROPERTIES); + if (entityWantsFarTrigger(targetProperties)) { return targetProperties; } } diff --git a/scripts/system/controllers/controllerModules/highlightNearbyEntities.js b/scripts/system/controllers/controllerModules/highlightNearbyEntities.js index bc09ebee7a..403b5d5149 100644 --- a/scripts/system/controllers/controllerModules/highlightNearbyEntities.js +++ b/scripts/system/controllers/controllerModules/highlightNearbyEntities.js @@ -8,11 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, Controller, RIGHT_HAND, LEFT_HAND, MyAvatar, getGrabPointSphereOffset, - makeRunningValues, Entities, enableDispatcherModule, disableDispatcherModule, makeDispatcherModuleParameters, - PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, - DEFAULT_SEARCH_SPHERE_DISTANCE, getGrabbableData, makeLaserParams, entityIsCloneable, Messages, print -*/ +/* global Script, MyAvatar, entityIsCloneable, Messages, print */ "use strict"; @@ -134,7 +130,7 @@ rightHighlightNearbyEntities.removeEntityFromHighlightList(data.entityID); } } catch (e) { - print("Failed to parse message"); + print("highlightNearbyEntities -- Failed to parse message: " + JSON.stringify(message)); } } else if (channel === 'Hifi-unhighlight-all') { leftHighlightNearbyEntities.clearAll(); diff --git a/scripts/system/controllers/controllerModules/hudOverlayPointer.js b/scripts/system/controllers/controllerModules/hudOverlayPointer.js index 04a3911e0b..efbca66d72 100644 --- a/scripts/system/controllers/controllerModules/hudOverlayPointer.js +++ b/scripts/system/controllers/controllerModules/hudOverlayPointer.js @@ -10,16 +10,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* global Script, Controller, RIGHT_HAND, LEFT_HAND, Mat4, MyAvatar, Vec3, Camera, Quat, - getGrabPointSphereOffset, getEnabledModuleByName, makeRunningValues, Entities, - enableDispatcherModule, disableDispatcherModule, entityIsDistanceGrabbable, - makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, - PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, - DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic, - getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays, isPointingAtUI, - makeLaserParams - -*/ +/* global Script, Controller, RIGHT_HAND, LEFT_HAND, HMD, makeLaserParams */ (function() { Script.include("/~/system/libraries/controllers.js"); var ControllerDispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js"); @@ -33,7 +24,7 @@ this.reticleMinY = MARGIN; this.reticleMaxY; this.parameters = ControllerDispatcherUtils.makeDispatcherModuleParameters( - 540, + 160, // Same as webSurfaceLaserInput. this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], [], 100, @@ -72,7 +63,6 @@ this.processLaser = function(controllerData) { var controllerLocation = controllerData.controllerLocations[this.hand]; - var otherModuleRunning = this.getOtherModule().running; if ((controllerData.triggerValues[this.hand] < ControllerDispatcherUtils.TRIGGER_ON_VALUE || !controllerLocation.valid) || this.pointingAtTablet(controllerData)) { return false; diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js index 15da1537a1..1917505bd8 100644 --- a/scripts/system/controllers/controllerModules/inEditMode.js +++ b/scripts/system/controllers/controllerModules/inEditMode.js @@ -8,9 +8,8 @@ /* jslint bitwise: true */ /* global Script, Controller, RIGHT_HAND, LEFT_HAND, enableDispatcherModule, disableDispatcherModule, makeRunningValues, - Messages, makeDispatcherModuleParameters, HMD, getGrabPointSphereOffset, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, - COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_ON_VALUE, - getEnabledModuleByName, PICK_MAX_DISTANCE, isInEditMode, Picks, makeLaserParams, Entities + Messages, makeDispatcherModuleParameters, HMD, getEnabledModuleByName, TRIGGER_ON_VALUE, isInEditMode, Picks, + makeLaserParams */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -24,12 +23,12 @@ Script.include("/~/system/libraries/utils.js"); this.triggerClicked = false; this.selectedTarget = null; this.reticleMinX = MARGIN; - this.reticleMaxX; + this.reticleMaxX = null; this.reticleMinY = MARGIN; - this.reticleMaxY; + this.reticleMaxY = null; this.parameters = makeDispatcherModuleParameters( - 160, + 165, // Lower priority than webSurfaceLaserInput and hudOverlayPointer. this.hand === RIGHT_HAND ? ["rightHand", "rightHandEquip", "rightHandTrigger"] : ["leftHand", "leftHandEquip", "leftHandTrigger"], [], 100, @@ -49,8 +48,8 @@ Script.include("/~/system/libraries/utils.js"); }; this.pointingAtTablet = function(objectID) { - return (HMD.tabletScreenID && objectID === HMD.tabletScreenID) - || (HMD.homeButtonID && objectID === HMD.homeButtonID); + return (HMD.tabletScreenID && objectID === HMD.tabletScreenID) || + (HMD.homeButtonID && objectID === HMD.homeButtonID); }; this.calculateNewReticlePosition = function(intersection) { @@ -128,29 +127,41 @@ Script.include("/~/system/libraries/utils.js"); }; this.run = function(controllerData) { - var tabletStylusInput = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightTabletStylusInput" : "LeftTabletStylusInput"); + var tabletStylusInput = getEnabledModuleByName(this.hand === RIGHT_HAND + ? "RightTabletStylusInput" : "LeftTabletStylusInput"); if (tabletStylusInput) { var tabletReady = tabletStylusInput.isReady(controllerData); - if (tabletReady.active) { return this.exitModule(); } } - var overlayLaser = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightWebSurfaceLaserInput" : "LeftWebSurfaceLaserInput"); - if (overlayLaser) { - var overlayLaserReady = overlayLaser.isReady(controllerData); + var webLaser = getEnabledModuleByName(this.hand === RIGHT_HAND + ? "RightWebSurfaceLaserInput" : "LeftWebSurfaceLaserInput"); + if (webLaser) { + var webLaserReady = webLaser.isReady(controllerData); var target = controllerData.rayPicks[this.hand].objectID; this.sendPointingAtData(controllerData); - if (overlayLaserReady.active && this.pointingAtTablet(target)) { + if (webLaserReady.active && this.pointingAtTablet(target)) { return this.exitModule(); } } - var nearOverlay = getEnabledModuleByName(this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay"); + if (!controllerData.triggerClicks[this.hand]) { // Don't grab if trigger pressed when laser starts intersecting. + var hudLaser = getEnabledModuleByName(this.hand === RIGHT_HAND + ? "RightHudOverlayPointer" : "LeftHudOverlayPointer"); + if (hudLaser) { + var hudLaserReady = hudLaser.isReady(controllerData); + if (hudLaserReady.active) { + return this.exitModule(); + } + } + } + + var nearOverlay = getEnabledModuleByName(this.hand === RIGHT_HAND + ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay"); if (nearOverlay) { var nearOverlayReady = nearOverlay.isReady(controllerData); - if (nearOverlayReady.active && HMD.tabletID && nearOverlay.grabbedThingID === HMD.tabletID) { return this.exitModule(); } diff --git a/scripts/system/controllers/controllerModules/inVREditMode.js b/scripts/system/controllers/controllerModules/inVREditMode.js index 7b78d5e1c4..65b6744646 100644 --- a/scripts/system/controllers/controllerModules/inVREditMode.js +++ b/scripts/system/controllers/controllerModules/inVREditMode.js @@ -8,7 +8,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, MyAvatar, RIGHT_HAND, LEFT_HAND, enableDispatcherModule, disableDispatcherModule, +/* global Script, HMD, Messages, MyAvatar, RIGHT_HAND, LEFT_HAND, enableDispatcherModule, disableDispatcherModule, makeDispatcherModuleParameters, makeRunningValues, getEnabledModuleByName, makeLaserParams */ @@ -19,9 +19,10 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); function InVREditMode(hand) { this.hand = hand; this.disableModules = false; - var NO_HAND_LASER = -1; // Invalid hand parameter so that default laser is not displayed. + this.running = false; + var NO_HAND_LASER = -1; // Invalid hand parameter so that standard laser is not displayed. this.parameters = makeDispatcherModuleParameters( - 200, // Not too high otherwise the tablet laser doesn't work. + 166, // Slightly lower priority than inEditMode. this.hand === RIGHT_HAND ? ["rightHand", "rightHandEquip", "rightHandTrigger"] : ["leftHand", "leftHandEquip", "leftHandTrigger"], @@ -31,8 +32,37 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); ); this.pointingAtTablet = function (objectID) { - return (HMD.tabletScreenID && objectID === HMD.tabletScreenID) - || (HMD.homeButtonID && objectID === HMD.homeButtonID); + return (HMD.tabletScreenID && objectID === HMD.tabletScreenID) || + (HMD.homeButtonID && objectID === HMD.homeButtonID); + }; + + // The Shapes app has a non-standard laser: in particular, the laser end dot displays on its own when the laser is + // pointing at the Shapes UI. The laser on/off is controlled by this module but the laser is implemented in the Shapes + // app. + // If, in the future, the Shapes app laser interaction is adopted as a standard UI style then the laser could be + // implemented in the controller modules along side the other laser styles. + var INVREDIT_MODULE_RUNNING = "Hifi-InVREdit-Module-Running"; + + this.runModule = function () { + if (!this.running) { + Messages.sendLocalMessage(INVREDIT_MODULE_RUNNING, JSON.stringify({ + hand: this.hand, + running: true + })); + this.running = true; + } + return makeRunningValues(true, [], []); + }; + + this.exitModule = function () { + if (this.running) { + Messages.sendLocalMessage(INVREDIT_MODULE_RUNNING, JSON.stringify({ + hand: this.hand, + running: false + })); + this.running = false; + } + return makeRunningValues(false, [], []); }; this.isReady = function (controllerData) { @@ -45,56 +75,69 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); this.run = function (controllerData) { // Default behavior if disabling is not enabled. if (!this.disableModules) { - return makeRunningValues(false, [], []); + return this.exitModule(); } // Tablet stylus. - var tabletStylusInput = getEnabledModuleByName(this.hand === RIGHT_HAND - ? "RightTabletStylusInput" - : "LeftTabletStylusInput"); + var tabletStylusInput = getEnabledModuleByName(this.hand === RIGHT_HAND ? + "RightTabletStylusInput" : + "LeftTabletStylusInput"); if (tabletStylusInput) { var tabletReady = tabletStylusInput.isReady(controllerData); if (tabletReady.active) { - return makeRunningValues(false, [], []); + return this.exitModule(); } } // Tablet surface. - var overlayLaser = getEnabledModuleByName(this.hand === RIGHT_HAND - ? "RightWebSurfaceLaserInput" - : "LeftWebSurfaceLaserInput"); + var overlayLaser = getEnabledModuleByName(this.hand === RIGHT_HAND ? + "RightWebSurfaceLaserInput" : + "LeftWebSurfaceLaserInput"); if (overlayLaser) { var overlayLaserReady = overlayLaser.isReady(controllerData); var target = controllerData.rayPicks[this.hand].objectID; if (overlayLaserReady.active && this.pointingAtTablet(target)) { - return makeRunningValues(false, [], []); + return this.exitModule(); } } // Tablet grabbing. - var nearOverlay = getEnabledModuleByName(this.hand === RIGHT_HAND - ? "RightNearParentingGrabOverlay" - : "LeftNearParentingGrabOverlay"); + var nearOverlay = getEnabledModuleByName(this.hand === RIGHT_HAND ? + "RightNearParentingGrabOverlay" : + "LeftNearParentingGrabOverlay"); if (nearOverlay) { var nearOverlayReady = nearOverlay.isReady(controllerData); if (nearOverlayReady.active && HMD.tabletID && nearOverlay.grabbedThingID === HMD.tabletID) { - return makeRunningValues(false, [], []); + return this.exitModule(); + } + } + + // HUD overlay. + if (!controllerData.triggerClicks[this.hand]) { + var hudLaser = getEnabledModuleByName(this.hand === RIGHT_HAND + ? "RightHudOverlayPointer" + : "LeftHudOverlayPointer"); + if (hudLaser) { + var hudLaserReady = hudLaser.isReady(controllerData); + if (hudLaserReady.active) { + return this.exitModule(); + } } } // Teleport. - var teleporter = getEnabledModuleByName(this.hand === RIGHT_HAND - ? "RightTeleporter" - : "LeftTeleporter"); + var teleporter = getEnabledModuleByName(this.hand === RIGHT_HAND ? + "RightTeleporter" : + "LeftTeleporter"); if (teleporter) { var teleporterReady = teleporter.isReady(controllerData); if (teleporterReady.active) { - return makeRunningValues(false, [], []); + return this.exitModule(); } } // Other behaviors are disabled. - return makeRunningValues(true, [], []); + return this.runModule(); }; } diff --git a/scripts/system/controllers/controllerModules/mouseHMD.js b/scripts/system/controllers/controllerModules/mouseHMD.js index 101a3502e1..27fe82ca19 100644 --- a/scripts/system/controllers/controllerModules/mouseHMD.js +++ b/scripts/system/controllers/controllerModules/mouseHMD.js @@ -10,6 +10,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +/* global Script, HMD, Reticle, Vec3, Controller */ + (function() { var ControllerDispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js"); @@ -121,7 +123,7 @@ if (this.mouseActivity.expired(now) || this.triggersPressed(controllerData, now) || !hmdActive) { if (!hmdActive) { Reticle.visible = true; - } else { + } else { Reticle.visible = false; } diff --git a/scripts/system/controllers/controllerModules/mouseHighlightEntities.js b/scripts/system/controllers/controllerModules/mouseHighlightEntities.js index ac57c8691f..59a68d98a4 100644 --- a/scripts/system/controllers/controllerModules/mouseHighlightEntities.js +++ b/scripts/system/controllers/controllerModules/mouseHighlightEntities.js @@ -12,7 +12,7 @@ /* jslint bitwise: true */ -/* global Script, print, Entities, Picks, HMD, Controller, MyAvatar, isInEditMode*/ +/* global Script, print, Entities, Messages, Picks, HMD, MyAvatar, isInEditMode, DISPATCHER_PROPERTIES */ (function() { @@ -46,11 +46,7 @@ var targetEntityID = pickResult.objectID; if (this.highlightedEntity !== targetEntityID) { - var targetProps = Entities.getEntityProperties(targetEntityID, [ - "dynamic", "shapeType", "position", - "rotation", "dimensions", "density", - "userData", "locked", "type", "href" - ]); + var targetProps = Entities.getEntityProperties(targetEntityID, DISPATCHER_PROPERTIES); if (this.highlightedEntity) { dispatcherUtils.unhighlightTargetEntity(this.highlightedEntity); diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js index a8de76aebd..5ced6080a2 100644 --- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -10,7 +10,8 @@ propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, entityIsGrabbable, Quat, Vec3, MSECS_PER_SEC, getControllerWorldLocation, makeDispatcherModuleParameters, makeRunningValues, TRIGGER_OFF_VALUE, NEAR_GRAB_RADIUS, findGroupParent, entityIsCloneable, propsAreCloneDynamic, cloneEntity, - HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, unhighlightTargetEntity, Uuid + HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, unhighlightTargetEntity, Uuid, + DISPATCHER_PROPERTIES */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -62,12 +63,12 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); var grabbableData = getGrabbableData(targetProps); - this.ignoreIK = grabbableData.ignoreIK; - this.kinematicGrab = grabbableData.kinematic; + this.grabFollowsController = grabbableData.grabFollowsController; + this.kinematicGrab = grabbableData.grabKinematic; var handRotation; var handPosition; - if (this.ignoreIK) { + if (this.grabFollowsController) { var controllerID = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; var controllerLocation = getControllerWorldLocation(controllerID, false); @@ -99,7 +100,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); ttl: ACTION_TTL, kinematic: this.kinematicGrab, kinematicSetVelocity: true, - ignoreIK: this.ignoreIK + ignoreIK: this.grabFollowsController }); if (this.actionID === Uuid.NULL) { this.actionID = null; @@ -136,7 +137,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); ttl: ACTION_TTL, kinematic: this.kinematicGrab, kinematicSetVelocity: true, - ignoreIK: this.ignoreIK + ignoreIK: this.grabFollowsController }); if (success) { this.actionTimeout = now + (ACTION_TTL * MSECS_PER_SEC); @@ -238,7 +239,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); var targetCloneable = entityIsCloneable(targetProps); if (targetCloneable) { var cloneID = cloneEntity(targetProps); - var cloneProps = Entities.getEntityProperties(cloneID); + var cloneProps = Entities.getEntityProperties(cloneID, DISPATCHER_PROPERTIES); this.targetEntityID = cloneID; this.startNearGrabAction(controllerData, cloneProps); } else { diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index cc88371441..bbdcbaaa64 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -12,7 +12,7 @@ findGroupParent, Vec3, cloneEntity, entityIsCloneable, propsAreCloneDynamic, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, BUMPER_ON_VALUE, findHandChildEntities, TEAR_AWAY_DISTANCE, MSECS_PER_SEC, TEAR_AWAY_CHECK_TIME, TEAR_AWAY_COUNT, distanceBetweenPointAndEntityBoundingBox, print, Uuid, highlightTargetEntity, unhighlightTargetEntity, - distanceBetweenEntityLocalPositionAndBoundingBox, GRAB_POINT_SPHERE_OFFSET + distanceBetweenEntityLocalPositionAndBoundingBox, getGrabbableData, getGrabPointSphereOffset, DISPATCHER_PROPERTIES */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -28,16 +28,8 @@ Script.include("/~/system/libraries/controllers.js"); var GRAB_POINT_SPHERE_OFFSET = { x: 0.04, y: 0.13, z: 0.039 }; // x = upward, y = forward, z = lateral function getGrabOffset(handController) { - var offset = GRAB_POINT_SPHERE_OFFSET; - if (handController === Controller.Standard.LeftHand) { - offset = { - x: -GRAB_POINT_SPHERE_OFFSET.x, - y: GRAB_POINT_SPHERE_OFFSET.y, - z: GRAB_POINT_SPHERE_OFFSET.z - }; - } - - offset.y = -GRAB_POINT_SPHERE_OFFSET.y; + var offset = getGrabPointSphereOffset(handController, true); + offset.y = -offset.y; return Vec3.multiply(MyAvatar.sensorToWorldScale, offset); } @@ -101,6 +93,7 @@ Script.include("/~/system/libraries/controllers.js"); }; this.startNearParentingGrabEntity = function (controllerData, targetProps) { + var grabData = getGrabbableData(targetProps); Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); unhighlightTargetEntity(this.targetEntityID); this.highlightedEntity = null; @@ -111,12 +104,11 @@ Script.include("/~/system/libraries/controllers.js"); Messages.sendLocalMessage('Hifi-unhighlight-entity', JSON.stringify(message)); var handJointIndex; - // if (this.ignoreIK) { - // handJointIndex = this.controllerJointIndex; - // } else { - // handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); - // } - handJointIndex = getControllerJointIndex(this.hand); + if (grabData.grabFollowsController) { + handJointIndex = getControllerJointIndex(this.hand); + } else { + handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); + } var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; Entities.callEntityMethod(targetProps.id, "startNearGrab", args); @@ -368,7 +360,7 @@ Script.include("/~/system/libraries/controllers.js"); if (this.cloneAllowed) { var cloneID = cloneEntity(targetProps); if (cloneID !== null) { - var cloneProps = Entities.getEntityProperties(cloneID); + var cloneProps = Entities.getEntityProperties(cloneID, DISPATCHER_PROPERTIES); this.grabbing = true; this.targetEntityID = cloneID; this.startNearParentingGrabEntity(controllerData, cloneProps); diff --git a/scripts/system/controllers/controllerModules/nearTrigger.js b/scripts/system/controllers/controllerModules/nearTrigger.js index 6a9cd9fbcd..f4e39cfbb9 100644 --- a/scripts/system/controllers/controllerModules/nearTrigger.js +++ b/scripts/system/controllers/controllerModules/nearTrigger.js @@ -16,7 +16,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); function entityWantsNearTrigger(props) { var grabbableData = getGrabbableData(props); - return grabbableData.triggerable || grabbableData.wantsTrigger; + return grabbableData.triggerable; } function NearTriggerEntity(hand) { diff --git a/scripts/system/controllers/controllerModules/scaleEntity.js b/scripts/system/controllers/controllerModules/scaleEntity.js index 9d54eef98e..24c05a1394 100644 --- a/scripts/system/controllers/controllerModules/scaleEntity.js +++ b/scripts/system/controllers/controllerModules/scaleEntity.js @@ -7,7 +7,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, Vec3, MyAvatar, RIGHT_HAND */ +/* global Script, Vec3, MyAvatar, Entities, RIGHT_HAND */ (function() { var dispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js"); diff --git a/scripts/system/controllers/controllerModules/stylusInput.js b/scripts/system/controllers/controllerModules/stylusInput.js index 0ca6cd1b04..1d36fb687f 100644 --- a/scripts/system/controllers/controllerModules/stylusInput.js +++ b/scripts/system/controllers/controllerModules/stylusInput.js @@ -5,11 +5,9 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, - enableDispatcherModule, disableDispatcherModule, makeRunningValues, - Messages, Quat, Vec3, getControllerWorldLocation, makeDispatcherModuleParameters, Overlays, ZERO_VEC, - HMD, INCHES_TO_METERS, DEFAULT_REGISTRATION_POINT, Settings, getGrabPointSphereOffset, - getEnabledModuleByName, Pointers, Picks, PickType +/* global Script, MyAvatar, Controller, Uuid, RIGHT_HAND, LEFT_HAND, enableDispatcherModule, disableDispatcherModule, + makeRunningValues, Vec3, makeDispatcherModuleParameters, Overlays, HMD, Settings, getEnabledModuleByName, Pointers, + Picks, PickType */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -200,7 +198,7 @@ Script.include("/~/system/libraries/controllers.js"); Overlays.hoverEnterOverlay.connect(mouseHoverEnter); Overlays.hoverLeaveOverlay.connect(mouseHoverLeave); - Overlays.mousePressOnOverlay.connect(mousePress); + Overlays.mousePressOnOverlay.connect(mousePress); this.cleanup = function () { leftTabletStylusInput.cleanup(); diff --git a/scripts/system/controllers/controllerModules/teleport.js b/scripts/system/controllers/controllerModules/teleport.js index bf5022cdaf..20a1e47bf2 100644 --- a/scripts/system/controllers/controllerModules/teleport.js +++ b/scripts/system/controllers/controllerModules/teleport.js @@ -10,7 +10,7 @@ /* jslint bitwise: true */ -/* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, +/* global Script, Entities, MyAvatar, Controller, Quat, RIGHT_HAND, LEFT_HAND, enableDispatcherModule, disableDispatcherModule, Messages, makeDispatcherModuleParameters, makeRunningValues, Vec3, HMD, Uuid, AvatarList, Picks, Pointers, PickType */ @@ -680,8 +680,8 @@ Script.include("/~/system/libraries/controllers.js"); this.teleportLocked = function () { // Lock teleport if in advanced movement mode and have just transitioned from pressing a direction button. - return Controller.getValue(Controller.Hardware.Application.AdvancedMovement) - && (_this.axisButtonStateX !== 0 || _this.axisButtonStateY !== 0); + return Controller.getValue(Controller.Hardware.Application.AdvancedMovement) && + (_this.axisButtonStateX !== 0 || _this.axisButtonStateY !== 0); }; this.buttonPress = function (value) { diff --git a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js index 2412e2fa1c..3d66e84d1f 100644 --- a/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js +++ b/scripts/system/controllers/controllerModules/webSurfaceLaserInput.js @@ -5,11 +5,9 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global Script, Entities, Controller, RIGHT_HAND, LEFT_HAND, enableDispatcherModule, disableDispatcherModule, - makeRunningValues, Messages, Quat, Vec3, makeDispatcherModuleParameters, Overlays, ZERO_VEC, HMD, - INCHES_TO_METERS, DEFAULT_REGISTRATION_POINT, getGrabPointSphereOffset, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, - COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_ON_VALUE, - TRIGGER_OFF_VALUE, getEnabledModuleByName, PICK_MAX_DISTANCE, ContextOverlay, Picks, makeLaserParams +/* global Script, Entities, enableDispatcherModule, disableDispatcherModule, makeRunningValues, + makeDispatcherModuleParameters, Overlays, HMD, TRIGGER_ON_VALUE, TRIGGER_OFF_VALUE, getEnabledModuleByName, + ContextOverlay, Picks, makeLaserParams, Settings, MyAvatar, RIGHT_HAND, LEFT_HAND, DISPATCHER_PROPERTIES */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -82,7 +80,7 @@ Script.include("/~/system/libraries/controllers.js"); return overlayType === "web3d" || triggerPressed; } } else if (intersection.type === Picks.INTERSECTED_ENTITY) { - var entityProperties = Entities.getEntityProperties(objectID); + var entityProperties = Entities.getEntityProperties(objectID, DISPATCHER_PROPERTIES); var entityType = entityProperties.type; var isLocked = entityProperties.locked; return entityType === "Web" && (!isLocked || triggerPressed); @@ -91,8 +89,9 @@ Script.include("/~/system/libraries/controllers.js"); }; this.deleteContextOverlay = function() { - var farGrabModule = getEnabledModuleByName(this.hand === RIGHT_HAND - ? "RightFarActionGrabEntity" : "LeftFarActionGrabEntity"); + var farGrabModule = getEnabledModuleByName(this.hand === RIGHT_HAND ? + "RightFarActionGrabEntity" : + "LeftFarActionGrabEntity"); if (farGrabModule) { var entityWithContextOverlay = farGrabModule.entityWithContextOverlay; diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index 6899577de2..73938af0d3 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +/* global Script, Menu */ + var CONTOLLER_SCRIPTS = [ "squeezeHands.js", "controllerDisplayManager.js", @@ -19,8 +21,8 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/nearParentGrabEntity.js", "controllerModules/nearParentGrabOverlay.js", "controllerModules/nearActionGrabEntity.js", - // "controllerModules/farActionGrabEntity.js", - // "controllerModules/farParentGrabEntity.js", + "controllerModules/farActionGrabEntityDynOnly.js", + "controllerModules/farParentGrabEntity.js", "controllerModules/stylusInput.js", "controllerModules/equipEntity.js", "controllerModules/nearTrigger.js", @@ -38,25 +40,22 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/mouseHighlightEntities.js" ]; -if (Settings.getValue("useFarGrabJoints", false)) { - CONTOLLER_SCRIPTS.push("controllerModules/farActionGrabEntityDynOnly.js"); - CONTOLLER_SCRIPTS.push("controllerModules/farParentGrabEntity.js"); -} else { - CONTOLLER_SCRIPTS.push("controllerModules/farActionGrabEntity.js"); -} - var DEBUG_MENU_ITEM = "Debug defaultScripts.js"; function runDefaultsTogether() { for (var j in CONTOLLER_SCRIPTS) { - Script.include(CONTOLLER_SCRIPTS[j]); + if (CONTOLLER_SCRIPTS.hasOwnProperty(j)) { + Script.include(CONTOLLER_SCRIPTS[j]); + } } } function runDefaultsSeparately() { for (var i in CONTOLLER_SCRIPTS) { - Script.load(CONTOLLER_SCRIPTS[i]); + if (CONTOLLER_SCRIPTS.hasOwnProperty(i)) { + Script.load(CONTOLLER_SCRIPTS[i]); + } } } diff --git a/scripts/system/controllers/grab.js b/scripts/system/controllers/grab.js index 48229ac9d9..a78a2971e9 100644 --- a/scripts/system/controllers/grab.js +++ b/scripts/system/controllers/grab.js @@ -14,9 +14,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* global MyAvatar, Entities, Script, Camera, Vec3, Reticle, Overlays, getEntityCustomData, Messages, Quat, Controller, - isInEditMode, HMD entityIsGrabbable, Picks, PickType, Pointers, unhighlightTargetEntity*/ - +/* global MyAvatar, Entities, Script, HMD, Camera, Vec3, Reticle, Overlays, getEntityCustomData, Messages, Quat, Controller, + isInEditMode, entityIsGrabbable, Picks, PickType, Pointers, unhighlightTargetEntity, DISPATCHER_PROPERTIES, + entityIsGrabbable, entityIsEquipped, getMainTabletIDs +*/ +/* jslint bitwise: true */ (function() { // BEGIN LOCAL_SCOPE @@ -37,7 +39,6 @@ var IDENTITY_QUAT = { z: 0, w: 0 }; -var GRABBABLE_DATA_KEY = "grabbableKey"; // shared with handControllerGrab.js var DEFAULT_GRABBABLE_DATA = { grabbable: true, @@ -339,16 +340,14 @@ Grabber.prototype.pressEvent = function(event) { return; } - var props = Entities.getEntityProperties(pickResults.objectID, ["dynamic", "userData", "locked", "type"]); + var props = Entities.getEntityProperties(pickResults.objectID, DISPATCHER_PROPERTIES); var isDynamic = props.dynamic; - var isGrabbable = props.grabbable; if (!entityIsGrabbable(props)) { // only grab grabbable objects return; } - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, pickResults.objectID, DEFAULT_GRABBABLE_DATA); - if (grabbableData.grabbable === false) { + if (!props.grab.grabbable) { return; } @@ -359,7 +358,7 @@ Grabber.prototype.pressEvent = function(event) { mouse.startDrag(event); var clickedEntity = pickResults.objectID; - var entityProperties = Entities.getEntityProperties(clickedEntity); + var entityProperties = Entities.getEntityProperties(clickedEntity, DISPATCHER_PROPERTIES); this.startPosition = entityProperties.position; this.lastRotation = entityProperties.rotation; this.madeDynamic = false; @@ -484,7 +483,7 @@ Grabber.prototype.moveEvent = function(event) { Grabber.prototype.moveEventProcess = function() { this.moveEventTimer = null; // see if something added/restored gravity - var entityProperties = Entities.getEntityProperties(this.entityID); + var entityProperties = Entities.getEntityProperties(this.entityID, DISPATCHER_PROPERTIES); if (!entityProperties || !entityProperties.gravity || HMD.active) { return; } diff --git a/scripts/system/controllers/handTouch.js b/scripts/system/controllers/handTouch.js index 97a24cb3f2..c706d054c1 100644 --- a/scripts/system/controllers/handTouch.js +++ b/scripts/system/controllers/handTouch.js @@ -11,7 +11,7 @@ /* jslint bitwise: true */ -/* global Script, Overlays, Controller, Vec3, MyAvatar, Entities +/* global Script, Overlays, Controller, Vec3, MyAvatar, Entities, RayPick */ (function () { @@ -22,8 +22,8 @@ var MSECONDS_AFTER_LOAD = 2000; var updateFingerWithIndex = 0; var untouchableEntities = []; - - // Keys to access finger data + + // Keys to access finger data var fingerKeys = ["pinky", "ring", "middle", "index", "thumb"]; // Additionally close the hands to achieve a grabbing effect @@ -47,7 +47,7 @@ left: new Palm(), right: new Palm() }; - + var handJointNames = {left: "LeftHand", right: "RightHand"}; // Store which fingers are touching - if all false restate the default poses diff --git a/scripts/system/controllers/squeezeHands.js b/scripts/system/controllers/squeezeHands.js index c9de473e28..69f44f46a9 100644 --- a/scripts/system/controllers/squeezeHands.js +++ b/scripts/system/controllers/squeezeHands.js @@ -11,6 +11,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +/* global Script, MyAvatar, Messages, Controller */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ (function() { // BEGIN LOCAL_SCOPE @@ -20,7 +21,7 @@ var lastRightTrigger = 0; var leftHandOverlayAlpha = 0; var rightHandOverlayAlpha = 0; -var CONTROLLER_DEAD_SPOT = 0.25; +// var CONTROLLER_DEAD_SPOT = 0.25; var TRIGGER_SMOOTH_TIMESCALE = 0.1; var OVERLAY_RAMP_RATE = 8.0; @@ -42,9 +43,9 @@ function clamp(val, min, max) { return Math.min(Math.max(val, min), max); } -function normalizeControllerValue(val) { - return clamp((val - CONTROLLER_DEAD_SPOT) / (1 - CONTROLLER_DEAD_SPOT), 0, 1); -} +// function normalizeControllerValue(val) { +// return clamp((val - CONTROLLER_DEAD_SPOT) / (1 - CONTROLLER_DEAD_SPOT), 0, 1); +// } function lerp(a, b, alpha) { return a * (1 - alpha) + b * alpha; diff --git a/scripts/system/controllers/touchControllerConfiguration.js b/scripts/system/controllers/touchControllerConfiguration.js index f22252f646..991b77b8af 100644 --- a/scripts/system/controllers/touchControllerConfiguration.js +++ b/scripts/system/controllers/touchControllerConfiguration.js @@ -8,7 +8,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* globals TOUCH_CONTROLLER_CONFIGURATION_LEFT:true TOUCH_CONTROLLER_CONFIGURATION_RIGHT:true */ +/* globals TOUCH_CONTROLLER_CONFIGURATION_LEFT:true, TOUCH_CONTROLLER_CONFIGURATION_RIGHT:true, + Quat, Vec3, Script, MyAvatar, Controller */ /* eslint camelcase: ["error", { "properties": "never" }] */ var leftBaseRotation = Quat.multiply( @@ -22,9 +23,9 @@ var rightBaseRotation = Quat.multiply( // keep these in sync with the values from OculusHelpers.cpp var CONTROLLER_LENGTH_OFFSET = 0.0762; -var CONTROLLER_LATERAL_OFFSET = 0.0381; -var CONTROLLER_VERTICAL_OFFSET = 0.0381; -var CONTROLLER_FORWARD_OFFSET = 0.1524; +// var CONTROLLER_LATERAL_OFFSET = 0.0381; +// var CONTROLLER_VERTICAL_OFFSET = 0.0381; +// var CONTROLLER_FORWARD_OFFSET = 0.1524; var leftBasePosition = Vec3.multiplyQbyV(leftBaseRotation, { x: -CONTROLLER_LENGTH_OFFSET / 2.0, diff --git a/scripts/system/controllers/viveControllerConfiguration.js b/scripts/system/controllers/viveControllerConfiguration.js index 60f0b6b88a..09fd8adacc 100644 --- a/scripts/system/controllers/viveControllerConfiguration.js +++ b/scripts/system/controllers/viveControllerConfiguration.js @@ -8,11 +8,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* globals VIVE_CONTROLLER_CONFIGURATION_LEFT:true VIVE_CONTROLLER_CONFIGURATION_RIGHT:true */ +/* globals VIVE_CONTROLLER_CONFIGURATION_LEFT:true, VIVE_CONTROLLER_CONFIGURATION_RIGHT:true, + MyAvatar, Quat, Script, Vec3, Controller */ /* eslint camelcase: ["error", { "properties": "never" }] */ -var LEFT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"); -var RIGHT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"); +// var LEFT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_LEFTHAND"); +// var RIGHT_JOINT_INDEX = MyAvatar.getJointIndex("_CONTROLLER_RIGHTHAND"); var leftBaseRotation = Quat.multiply( Quat.fromPitchYawRollDegrees(0, 0, 45), @@ -58,10 +59,10 @@ var viveNaturalPosition = { }; var BASE_URL = Script.resourcesPath(); -var TIP_TEXTURE_BASE_URL = BASE_URL + "meshes/controller/vive_tips.fbm/"; +// var TIP_TEXTURE_BASE_URL = BASE_URL + "meshes/controller/vive_tips.fbm/"; var viveModelURL = BASE_URL + "meshes/controller/vive_body.fbx"; -var viveTipsModelURL = BASE_URL + "meshes/controller/vive_tips.fbx"; +// var viveTipsModelURL = BASE_URL + "meshes/controller/vive_tips.fbx"; var viveTriggerModelURL = "meshes/controller/vive_trigger.fbx"; VIVE_CONTROLLER_CONFIGURATION_LEFT = { @@ -340,4 +341,3 @@ VIVE_CONTROLLER_CONFIGURATION_RIGHT = { } ] }; - diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 27858722ec..686a516504 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -345,11 +345,15 @@ var toolBar = (function () { position = grid.snapToSurface(grid.snapToGrid(position, false, dimensions), dimensions); properties.position = position; + + if (!properties.grab) { + properties.grab = {}; + } if (Menu.isOptionChecked(MENU_CREATE_ENTITIES_GRABBABLE) && !(properties.type === "Zone" || properties.type === "Light" || properties.type === "ParticleEffect")) { - properties.userData = JSON.stringify({ grabbableKey: { grabbable: true } }); + properties.grab.grabbable = true; } else { - properties.userData = JSON.stringify({ grabbableKey: { grabbable: false } }); + properties.grab.grabbable = false; } SelectionManager.saveProperties(); @@ -1405,7 +1409,6 @@ Script.scriptEnding.connect(function () { Settings.setValue(SETTING_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); Settings.setValue(SETTING_SHOW_ZONES_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); - Settings.setValue(SETTING_EDIT_PREFIX + MENU_CREATE_ENTITIES_GRABBABLE, Menu.isOptionChecked(MENU_CREATE_ENTITIES_GRABBABLE)); Settings.setValue(SETTING_EDIT_PREFIX + MENU_ALLOW_SELECTION_LARGE, Menu.isOptionChecked(MENU_ALLOW_SELECTION_LARGE)); Settings.setValue(SETTING_EDIT_PREFIX + MENU_ALLOW_SELECTION_SMALL, Menu.isOptionChecked(MENU_ALLOW_SELECTION_SMALL)); Settings.setValue(SETTING_EDIT_PREFIX + MENU_ALLOW_SELECTION_LIGHTS, Menu.isOptionChecked(MENU_ALLOW_SELECTION_LIGHTS)); @@ -1706,7 +1709,7 @@ function onPromptTextChanged(prompt) { } } -function handeMenuEvent(menuItem) { +function handleMenuEvent(menuItem) { if (menuItem === "Allow Selecting of Small Models") { allowSmallModels = Menu.isOptionChecked("Allow Selecting of Small Models"); } else if (menuItem === "Allow Selecting of Large Models") { @@ -1746,6 +1749,8 @@ function handeMenuEvent(menuItem) { entityIconOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_AND_PARTICLES_IN_EDIT_MODE)); } else if (menuItem === MENU_SHOW_ZONES_IN_EDIT_MODE) { Entities.setDrawZoneBoundaries(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE)); + } else if (menuItem === MENU_CREATE_ENTITIES_GRABBABLE) { + Settings.setValue(SETTING_EDIT_PREFIX + menuItem, Menu.isOptionChecked(menuItem)); } tooltip.show(false); } @@ -1871,7 +1876,7 @@ function importSVO(importURL) { } Window.svoImportRequested.connect(importSVO); -Menu.menuItemEvent.connect(handeMenuEvent); +Menu.menuItemEvent.connect(handleMenuEvent); var keyPressEvent = function (event) { if (isActive) { diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 744150253d..8b27efa1fe 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -134,8 +134,8 @@
- - + +
diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index de9027586e..2d2e4d5675 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -6,8 +6,8 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global alert, augmentSpinButtons, clearTimeout, console, document, Element, EventBridge, - HifiEntityUI, JSONEditor, openEventBridge, setTimeout, window, _ $ */ +/* global alert, augmentSpinButtons, clearTimeout, document, Element, EventBridge, + JSONEditor, openEventBridge, setTimeout, window, $ */ var PI = 3.14159265358979; var DEGREES_TO_RADIANS = PI / 180.0; @@ -364,12 +364,6 @@ function multiDataUpdater(groupName, updateKeyPair, userDataElement, defaults, r updateProperties(properties); } -function userDataChanger(groupName, keyName, values, userDataElement, defaultValue, removeKeys) { - var val = {}, def = {}; - val[keyName] = values; - def[keyName] = defaultValue; - multiDataUpdater(groupName, val, userDataElement, def, removeKeys); -} function setMaterialDataFromEditor(noUpdate) { var json = null; @@ -712,6 +706,8 @@ function loaded() { var elCollisionSoundURL = document.getElementById("property-collision-sound-url"); var elGrabbable = document.getElementById("property-grabbable"); + var elTriggerable = document.getElementById("property-triggerable"); + var elGrabFollowsController = document.getElementById("property-grab-follows-controller"); var elCloneable = document.getElementById("property-cloneable"); var elCloneableDynamic = document.getElementById("property-cloneable-dynamic"); @@ -720,9 +716,6 @@ function loaded() { var elCloneableLifetime = document.getElementById("property-cloneable-lifetime"); var elCloneableLimit = document.getElementById("property-cloneable-limit"); - var elTriggerable = document.getElementById("property-triggerable"); - var elIgnoreIK = document.getElementById("property-ignore-ik"); - var elLifetime = document.getElementById("property-lifetime"); var elScriptURL = document.getElementById("property-script-url"); var elScriptTimestamp = document.getElementById("property-script-timestamp"); @@ -992,7 +985,7 @@ function loaded() { elGrabbable.checked = false; elTriggerable.checked = false; - elIgnoreIK.checked = false; + elGrabFollowsController.checked = false; elCloneable.checked = false; elCloneableDynamic.checked = false; @@ -1037,7 +1030,7 @@ function loaded() { elCompoundShapeURL.value = ""; elShapeType.value = "none"; setDropdownText(elShapeType); - elModelAnimationURL.value = "" + elModelAnimationURL.value = ""; elModelAnimationPlaying.checked = false; elModelAnimationFPS.value = ""; elModelAnimationFrame.value = ""; @@ -1254,10 +1247,9 @@ function loaded() { elCollideMyAvatar.checked = properties.collidesWith.indexOf("myAvatar") > -1; elCollideOtherAvatar.checked = properties.collidesWith.indexOf("otherAvatar") > -1; - elGrabbable.checked = properties.dynamic; - - elTriggerable.checked = false; - elIgnoreIK.checked = true; + elGrabbable.checked = properties.grab.grabbable; + elTriggerable.checked = properties.grab.triggerable; + elGrabFollowsController.checked = properties.grab.grabFollowsController; elCloneable.checked = properties.cloneable; elCloneableDynamic.checked = properties.cloneDynamic; @@ -1266,42 +1258,6 @@ function loaded() { elCloneableLimit.value = properties.cloneLimit; elCloneableLifetime.value = properties.cloneLifetime; - var grabbablesSet = false; - var parsedUserData = {}; - try { - parsedUserData = JSON.parse(properties.userData); - - if ("grabbableKey" in parsedUserData) { - grabbablesSet = true; - var grabbableData = parsedUserData.grabbableKey; - if ("grabbable" in grabbableData) { - elGrabbable.checked = grabbableData.grabbable; - } else { - elGrabbable.checked = true; - } - if ("triggerable" in grabbableData) { - elTriggerable.checked = grabbableData.triggerable; - } else if ("wantsTrigger" in grabbableData) { - elTriggerable.checked = grabbableData.wantsTrigger; - } else { - elTriggerable.checked = false; - } - if ("ignoreIK" in grabbableData) { - elIgnoreIK.checked = grabbableData.ignoreIK; - } else { - elIgnoreIK.checked = true; - } - } - } catch (e) { - // TODO: What should go here? - } - if (!grabbablesSet) { - elGrabbable.checked = true; - elTriggerable.checked = false; - elIgnoreIK.checked = true; - elCloneable.checked = false; - } - elCollisionSoundURL.value = properties.collisionSoundURL; elLifetime.value = properties.lifetime; elScriptURL.value = properties.script; @@ -1669,26 +1625,18 @@ function loaded() { updateCheckedSubProperty("collidesWith", properties.collidesWith, elCollideOtherAvatar, 'otherAvatar'); }); - elGrabbable.addEventListener('change', function() { - if (elCloneable.checked) { - elGrabbable.checked = false; - } - userDataChanger("grabbableKey", "grabbable", elGrabbable, elUserData, true); - }); - + + elGrabbable.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('grab', 'grabbable')); + elTriggerable.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('grab', 'triggerable')); + elGrabFollowsController.addEventListener('change', + createEmitGroupCheckedPropertyUpdateFunction('grab', 'grabFollowsController')); + elCloneable.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneable')); elCloneableDynamic.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneDynamic')); elCloneableAvatarEntity.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneAvatarEntity')); elCloneableLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('cloneLifetime')); elCloneableLimit.addEventListener('change', createEmitNumberPropertyUpdateFunction('cloneLimit')); - elTriggerable.addEventListener('change', function() { - userDataChanger("grabbableKey", "triggerable", elTriggerable, elUserData, false, ['wantsTrigger']); - }); - elIgnoreIK.addEventListener('change', function() { - userDataChanger("grabbableKey", "ignoreIK", elIgnoreIK, elUserData, true); - }); - elCollisionSoundURL.addEventListener('change', createEmitTextPropertyUpdateFunction('collisionSoundURL')); elLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('lifetime')); diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index c201a251d0..8e5b41deda 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -129,9 +129,7 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location, visible) { url: modelURL, // for overlay grabbable: true, // for overlay loadPriority: 10.0, // for overlay - userData: JSON.stringify({ - "grabbableKey": {"grabbable": true} - }), + grab: { grabbable: true }, dimensions: { x: tabletWidth, y: tabletHeight, z: tabletDepth }, parentID: MyAvatar.SELF_ID, visible: visible, diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index c34fd76802..e9d5255d28 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -48,6 +48,7 @@ BUMPER_ON_VALUE:true, getEntityParents:true, findHandChildEntities:true, + findFarGrabJointChildEntities:true, makeLaserParams:true, TEAR_AWAY_DISTANCE:true, TEAR_AWAY_COUNT:true, @@ -127,13 +128,25 @@ DISPATCHER_PROPERTIES = [ "parentJointIndex", "density", "dimensions", - "userData", "type", "href", "cloneable", "cloneDynamic", "localPosition", - "localRotation" + "localRotation", + "grab.grabbable", + "grab.grabKinematic", + "grab.grabFollowsController", + "grab.triggerable", + "grab.equippable", + "grab.equippableLeftPosition", + "grab.equippableLeftRotation", + "grab.equippableRightPosition", + "grab.equippableRightRotation", + "grab.equippableIndicatorURL", + "grab.equippableIndicatorScale", + "grab.equippableIndicatorOffset", + "userData" ]; // priority -- a lower priority means the module will be asked sooner than one with a higher priority in a given update step @@ -215,25 +228,56 @@ getGrabbableData = function (ggdProps) { } catch (err) { userDataParsed = {}; } + if (userDataParsed.grabbableKey) { grabbableData = userDataParsed.grabbableKey; + } else { + grabbableData = ggdProps.grab; } + + // extract grab-related properties, provide defaults if any are missing if (!grabbableData.hasOwnProperty("grabbable")) { grabbableData.grabbable = true; } - if (!grabbableData.hasOwnProperty("ignoreIK")) { - grabbableData.ignoreIK = true; + // kinematic has been renamed to grabKinematic + if (!grabbableData.hasOwnProperty("grabKinematic") && + !grabbableData.hasOwnProperty("kinematic")) { + grabbableData.grabKinematic = true; } - if (!grabbableData.hasOwnProperty("kinematic")) { - grabbableData.kinematic = true; + if (!grabbableData.hasOwnProperty("grabKinematic")) { + grabbableData.grabKinematic = grabbableData.kinematic; } - if (!grabbableData.hasOwnProperty("wantsTrigger")) { - grabbableData.wantsTrigger = false; + // ignoreIK has been renamed to grabFollowsController + if (!grabbableData.hasOwnProperty("grabFollowsController") && + !grabbableData.hasOwnProperty("ignoreIK")) { + grabbableData.grabFollowsController = true; } - if (!grabbableData.hasOwnProperty("triggerable")) { + if (!grabbableData.hasOwnProperty("grabFollowsController")) { + grabbableData.grabFollowsController = grabbableData.ignoreIK; + } + // wantsTrigger has been renamed to triggerable + if (!grabbableData.hasOwnProperty("triggerable") && + !grabbableData.hasOwnProperty("wantsTrigger")) { grabbableData.triggerable = false; } - + if (!grabbableData.hasOwnProperty("triggerable")) { + grabbableData.triggerable = grabbableData.wantsTrigger; + } + if (!grabbableData.hasOwnProperty("equippable")) { + grabbableData.equippable = false; + } + if (!grabbableData.hasOwnProperty("equippableLeftPosition")) { + grabbableData.equippableLeftPosition = { x: 0, y: 0, z: 0 }; + } + if (!grabbableData.hasOwnProperty("equippableLeftRotation")) { + grabbableData.equippableLeftPosition = { x: 0, y: 0, z: 0, w: 1 }; + } + if (!grabbableData.hasOwnProperty("equippableRightPosition")) { + grabbableData.equippableRightPosition = { x: 0, y: 0, z: 0 }; + } + if (!grabbableData.hasOwnProperty("equippableRightRotation")) { + grabbableData.equippableRightPosition = { x: 0, y: 0, z: 0, w: 1 }; + } return grabbableData; }; @@ -417,6 +461,18 @@ findHandChildEntities = function(hand) { }); }; +findFarGrabJointChildEntities = function(hand) { + // find children of avatar's far-grab joint + var farGrabJointIndex = MyAvatar.getJointIndex(hand === RIGHT_HAND ? "_FARGRAB_RIGHTHAND" : "_FARGRAB_LEFTHAND"); + var children = Entities.getChildrenIDsOfJoint(MyAvatar.sessionUUID, farGrabJointIndex); + children = children.concat(Entities.getChildrenIDsOfJoint(MyAvatar.SELF_ID, farGrabJointIndex)); + + return children.filter(function (childID) { + var childType = Entities.getNestableType(childID); + return childType == "entity"; + }); +}; + distanceBetweenEntityLocalPositionAndBoundingBox = function(entityProps, jointGrabOffset) { var DEFAULT_REGISTRATION_POINT = { x: 0.5, y: 0.5, z: 0.5 }; var rotInv = Quat.inverse(entityProps.localRotation); diff --git a/scripts/system/libraries/controllers.js b/scripts/system/libraries/controllers.js index cc20c196aa..be7d22e073 100644 --- a/scripts/system/libraries/controllers.js +++ b/scripts/system/libraries/controllers.js @@ -5,20 +5,25 @@ // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global MyAvatar, Vec3, Controller, Quat */ +/* global MyAvatar, Vec3, HMD, Controller, Camera, Quat, Settings, + getGrabPointSphereOffset:true, + setGrabCommunications:true, + getGrabCommunications:true, + getControllerWorldLocation:true + */ var GRAB_COMMUNICATIONS_SETTING = "io.highfidelity.isFarGrabbing"; setGrabCommunications = function setFarGrabCommunications(on) { Settings.setValue(GRAB_COMMUNICATIONS_SETTING, on ? "on" : ""); -} +}; getGrabCommunications = function getFarGrabCommunications() { return !!Settings.getValue(GRAB_COMMUNICATIONS_SETTING, ""); -} +}; // this offset needs to match the one in libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp:378 -var GRAB_POINT_SPHERE_OFFSET = { x: 0.04, y: 0.13, z: 0.039 }; // x = upward, y = forward, z = lateral getGrabPointSphereOffset = function(handController, ignoreSensorToWorldScale) { + var GRAB_POINT_SPHERE_OFFSET = { x: 0.04, y: 0.13, z: 0.039 }; // x = upward, y = forward, z = lateral var offset = GRAB_POINT_SPHERE_OFFSET; if (handController === Controller.Standard.LeftHand) { offset = { @@ -39,7 +44,7 @@ getControllerWorldLocation = function (handController, doOffset) { var orientation; var position; var valid = false; - + if (handController >= 0) { var pose = Controller.getPoseValue(handController); valid = pose.valid; diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index a13c645b81..5f5225418f 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -215,7 +215,9 @@ SelectionManager = (function() { var grabJointNames = [ 'RightHand', 'LeftHand', '_CONTROLLER_RIGHTHAND', '_CONTROLLER_LEFTHAND', - '_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND', '_CAMERA_RELATIVE_CONTROLLER_LEFTHAND']; + '_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND', '_CAMERA_RELATIVE_CONTROLLER_LEFTHAND', + '_FARGRAB_RIGHTHAND', '_FARGRAB_LEFTHAND', '_FARGRAB_MOUSE' + ]; for (var i = 0; i < grabJointNames.length; ++i) { if (avatar.getJointIndex(grabJointNames[i]) === properties.parentJointIndex) { diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index fdc00c1ebf..a2f0d074f0 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -23,12 +23,17 @@ miniState = null, // Hands. + NO_HAND = -1, LEFT_HAND = 0, RIGHT_HAND = 1, HAND_NAMES = ["LeftHand", "RightHand"], - // Miscellaneous. + // Track controller grabbing state. HIFI_OBJECT_MANIPULATION_CHANNEL = "Hifi-Object-Manipulation", + grabbingHand = NO_HAND, + grabbedItem = null, + + // Miscellaneous. tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"), DEBUG = false; @@ -737,37 +742,11 @@ setState(MINI_VISIBLE); } - function enterMiniShowing(hand) { - miniHand = hand; - ui.show(miniHand); - miniScaleStart = Date.now(); - miniScaleTimer = Script.setTimeout(scaleMiniUp, MINI_SCALE_TIMEOUT); - } - - function updateMiniShowing() { - if (HMD.showTablet) { - setState(MINI_HIDDEN); - } - } - - function exitMiniShowing() { - if (miniScaleTimer) { - Script.clearTimeout(miniScaleTimer); - miniScaleTimer = null; - } - } - - function updateMiniVisible() { + function checkMiniVisibility() { var showLeft, showRight; - // Hide mini tablet if tablet proper has been displayed by other means. - if (HMD.showTablet) { - setState(MINI_HIDDEN); - return; - } - - // Check that the mini tablet should still be visible and if so then ensure it's on the hand that the camera is + // Check that the mini tablet should still be visible and if so then ensure it's on the hand that the camera is // gazing at. showLeft = shouldShowMini(LEFT_HAND); showRight = shouldShowMini(RIGHT_HAND); @@ -790,8 +769,47 @@ setState(MINI_HIDING); } } else { - setState(MINI_HIDING); + if (grabbedItem === null || grabbingHand !== miniHand) { + setState(MINI_HIDING); + } else { + setState(MINI_HIDDEN); + } } + } + + function enterMiniShowing(hand) { + miniHand = hand; + ui.show(miniHand); + miniScaleStart = Date.now(); + miniScaleTimer = Script.setTimeout(scaleMiniUp, MINI_SCALE_TIMEOUT); + } + + function updateMiniShowing() { + // Hide mini tablet if tablet proper has been displayed by other means. + if (HMD.showTablet) { + setState(MINI_HIDDEN); + } + + // Hide mini tablet if it should no longer be visible. + checkMiniVisibility(); + } + + function exitMiniShowing() { + if (miniScaleTimer) { + Script.clearTimeout(miniScaleTimer); + miniScaleTimer = null; + } + } + + function updateMiniVisible() { + // Hide mini tablet if tablet proper has been displayed by other means. + if (HMD.showTablet) { + setState(MINI_HIDDEN); + return; + } + + // Hide mini tablet if it should no longer be visible. + checkMiniVisibility(); // If state hasn't changed, update mini tablet rotation. if (miniState === MINI_VISIBLE) { @@ -973,6 +991,21 @@ return; } + // Track grabbed state and item. + switch (message.action) { + case "grab": + grabbingHand = HAND_NAMES.indexOf(message.joint); + grabbedItem = message.grabbedEntity; + break; + case "release": + grabbingHand = NO_HAND; + grabbedItem = null; + break; + default: + error("Unexpected grab message!"); + return; + } + if (message.grabbedEntity !== HMD.tabletID && message.grabbedEntity !== ui.getMiniTabletID()) { return; } diff --git a/scripts/system/redirectOverlays.js b/scripts/system/redirectOverlays.js index b1180e0cd0..bb537bee0e 100644 --- a/scripts/system/redirectOverlays.js +++ b/scripts/system/redirectOverlays.js @@ -5,14 +5,20 @@ "Oops! Protocol version mismatch.", "Oops! Not authorized to join domain.", "Oops! Connection timed out.", + "Oops! The domain is full.", "Oops! Something went wrong." ]; var PROTOCOL_VERSION_MISMATCH = 1; var NOT_AUTHORIZED = 3; + var DOMAIN_FULL = 4; var TIMEOUT = 5; - var hardRefusalErrors = [PROTOCOL_VERSION_MISMATCH, - NOT_AUTHORIZED, TIMEOUT]; + var hardRefusalErrors = [ + PROTOCOL_VERSION_MISMATCH, + NOT_AUTHORIZED, + TIMEOUT, + DOMAIN_FULL + ]; var timer = null; var isErrorState = false; @@ -26,7 +32,7 @@ return ERROR_MESSAGE_MAP[errorMessageMapIndex]; } else { // some other text. - return ERROR_MESSAGE_MAP[4]; + return ERROR_MESSAGE_MAP[ERROR_MESSAGE_MAP.length - 1]; } }; diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 3d744b3bd2..ed6ff80398 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -44,7 +44,10 @@ try { } function removeFromStoryIDsToMaybeDelete(story_id) { - storyIDsToMaybeDelete.splice(storyIDsToMaybeDelete.indexOf(story_id), 1); + story_id = parseInt(story_id); + if (storyIDsToMaybeDelete.indexOf(story_id) > -1) { + storyIDsToMaybeDelete.splice(storyIDsToMaybeDelete.indexOf(story_id), 1); + } print('storyIDsToMaybeDelete[] now:', JSON.stringify(storyIDsToMaybeDelete)); } @@ -258,6 +261,7 @@ function onMessage(message) { } break; case 'removeFromStoryIDsToMaybeDelete': + console.log("Facebook OR Twitter button clicked for story_id " + message.story_id); removeFromStoryIDsToMaybeDelete(message.story_id); break; default: @@ -312,9 +316,7 @@ function printToPolaroid(image_url) { "dynamic": true, "collisionsWillMove": true, - "userData": { - "grabbableKey": { "grabbable" : true } - } + "grab": { "grabbable": true } }; var polaroid = Entities.addEntity(properties); @@ -335,11 +337,13 @@ function fillImageDataFromPrevious() { var previousAnimatedSnapStoryID = Settings.getValue("previousAnimatedSnapStoryID"); var previousAnimatedSnapBlastingDisabled = Settings.getValue("previousAnimatedSnapBlastingDisabled"); var previousAnimatedSnapHifiSharingDisabled = Settings.getValue("previousAnimatedSnapHifiSharingDisabled"); + snapshotOptions = { containsGif: previousAnimatedSnapPath !== "", processingGif: false, shouldUpload: false, - canBlast: snapshotDomainID === Settings.getValue("previousSnapshotDomainID"), + canBlast: snapshotDomainID === Settings.getValue("previousSnapshotDomainID") && + snapshotDomainID === location.domainID, isLoggedIn: isLoggedIn }; imageData = []; @@ -371,7 +375,8 @@ function snapshotUploaded(isError, reply) { isGif = fileExtensionMatches(imageURL, "gif"), ignoreGifSnapshotData = false, ignoreStillSnapshotData = false; - storyIDsToMaybeDelete.push(storyID); + storyIDsToMaybeDelete.push(parseInt(storyID)); + print('storyIDsToMaybeDelete[] now:', JSON.stringify(storyIDsToMaybeDelete)); if (isGif) { if (mostRecentGifSnapshotFilename !== replyJson.user_story.details.original_image_file_name) { ignoreGifSnapshotData = true; diff --git a/scripts/tutorials/createCow.js b/scripts/tutorials/createCow.js index 16498e0e8c..0f034ecefa 100644 --- a/scripts/tutorials/createCow.js +++ b/scripts/tutorials/createCow.js @@ -45,11 +45,7 @@ var cow = Entities.addEntity({ lifetime: 3600, shapeType: "box", script: SCRIPT_URL, - userData: JSON.stringify({ - grabbableKey: { - grabbable: true - } - }) + grab: { grabbable: true } }); -Script.stop(); \ No newline at end of file +Script.stop(); diff --git a/scripts/tutorials/createFlashlight.js b/scripts/tutorials/createFlashlight.js index f3e1e72182..329be56af7 100644 --- a/scripts/tutorials/createFlashlight.js +++ b/scripts/tutorials/createFlashlight.js @@ -37,11 +37,6 @@ var flashlight = Entities.addEntity({ shapeType: 'box', lifetime: 3600, script: SCRIPT_URL, - userData: JSON.stringify({ - grabbableKey: { - invertSolidWhileHeld: true - } - }) }); -Script.stop(); \ No newline at end of file +Script.stop(); diff --git a/scripts/tutorials/createGolfClub.js b/scripts/tutorials/createGolfClub.js index 21e60f26ef..4135b6680d 100644 --- a/scripts/tutorials/createGolfClub.js +++ b/scripts/tutorials/createGolfClub.js @@ -53,35 +53,34 @@ var golfClubProperties = { y: -5.0, z: 0 }, - userData: JSON.stringify({ - wearable: { - joints: { - LeftHand: [{ - x: -0.1631782054901123, - y: 0.44648152589797974, - z: 0.10100018978118896 - }, { - x: -0.9181621670722961, - y: -0.0772884339094162, - z: -0.3870723247528076, - w: -0.0343472845852375 - }], - RightHand: [{ - x: 0.16826771199703217, - y: 0.4757269620895386, - z: 0.07139724493026733 - }, { - x: -0.7976328134536743, - y: -0.0011603273451328278, - z: 0.6030101776123047, - w: -0.012610925361514091 - }] - } + grab: { + equippable: true, + equippableLeftPosition: { + x: -0.1631782054901123, + y: 0.44648152589797974, + z: 0.10100018978118896 + }, + equippableLeftRotation: { + x: -0.9181621670722961, + y: -0.0772884339094162, + z: -0.3870723247528076, + w: -0.0343472845852375 + }, + equippableRightPosition: { + x: 0.16826771199703217, + y: 0.4757269620895386, + z: 0.07139724493026733 + }, + equippableRightRotation: { + x: -0.7976328134536743, + y: -0.0011603273451328278, + z: 0.6030101776123047, + w: -0.012610925361514091 } - }) -} + } +}; var golfClub = Entities.addEntity(golfClubProperties); -Script.stop(); \ No newline at end of file +Script.stop(); diff --git a/scripts/tutorials/createPingPongGun.js b/scripts/tutorials/createPingPongGun.js index c86a78e96d..927738f29e 100644 --- a/scripts/tutorials/createPingPongGun.js +++ b/scripts/tutorials/createPingPongGun.js @@ -37,35 +37,31 @@ var pingPongGunProperties = { }, lifetime: 3600, dynamic: true, - userData: JSON.stringify({ - grabbableKey: { - invertSolidWhileHeld: true + grab: { + equippable: true, + equippableLeftPosition: { + x: 0.09151676297187805, + y: 0.13639454543590546, + z: 0.09354984760284424 }, - wearable: { - joints: { - RightHand: [{ - x: 0.1177130937576294, - y: 0.12922893464565277, - z: 0.08307232707738876 - }, { - x: 0.4934672713279724, - y: 0.3605862259864807, - z: 0.6394805908203125, - w: -0.4664038419723511 - }], - LeftHand: [{ - x: 0.09151676297187805, - y: 0.13639454543590546, - z: 0.09354984760284424 - }, { - x: -0.19628101587295532, - y: 0.6418180465698242, - z: 0.2830369472503662, - w: 0.6851521730422974 - }] - } + equippableLeftRotation: { + x: -0.19628101587295532, + y: 0.6418180465698242, + z: 0.2830369472503662, + w: 0.6851521730422974 + }, + equippableRightPosition: { + x: 0.1177130937576294, + y: 0.12922893464565277, + z: 0.08307232707738876 + }, + equippableRightRotation: { + x: 0.4934672713279724, + y: 0.3605862259864807, + z: 0.6394805908203125, + w: -0.4664038419723511 } - }) + } } var pingPongGun = Entities.addEntity(pingPongGunProperties); diff --git a/scripts/tutorials/createPistol.js b/scripts/tutorials/createPistol.js index 8851f53d09..0912645c35 100644 --- a/scripts/tutorials/createPistol.js +++ b/scripts/tutorials/createPistol.js @@ -37,38 +37,34 @@ var pistolProperties = { restitution: 0, damping: 0.5, collisionSoundURL: COLLISION_SOUND_URL, - userData: JSON.stringify({ - grabbableKey: { - invertSolidWhileHeld: true + grab: { + equippable: true, + equippableLeftPosition: { + x: 0.1802254319190979, + y: 0.13442856073379517, + z: 0.08504903316497803 }, - wearable: { - joints: { - RightHand: [{ - x: 0.07079616189002991, - y: 0.20177987217903137, - z: 0.06374628841876984 - }, { - x: -0.5863648653030396, - y: -0.46007341146469116, - z: 0.46949487924575806, - w: -0.4733745753765106 - }], - LeftHand: [{ - x: 0.1802254319190979, - y: 0.13442856073379517, - z: 0.08504903316497803 - }, { - x: 0.2198076844215393, - y: -0.7377811074256897, - z: 0.2780133783817291, - w: 0.574519157409668 - }] - } + equippableLeftRotation: { + x: 0.2198076844215393, + y: -0.7377811074256897, + z: 0.2780133783817291, + w: 0.574519157409668 + }, + equippableRightPosition: { + x: 0.07079616189002991, + y: 0.20177987217903137, + z: 0.06374628841876984 + }, + equippableRightRotation: { + x: -0.5863648653030396, + y: -0.46007341146469116, + z: 0.46949487924575806, + w: -0.4733745753765106 } - }) + } }; var pistol = Entities.addEntity(pistolProperties); -Script.stop(); \ No newline at end of file +Script.stop(); diff --git a/scripts/tutorials/createSwords.js b/scripts/tutorials/createSwords.js index 749df3c5ed..f375939dc8 100644 --- a/scripts/tutorials/createSwords.js +++ b/scripts/tutorials/createSwords.js @@ -29,11 +29,7 @@ var sword1 = Entities.addEntity({ angularDamping: 0, damping: 0, script: SCRIPT_URL, - userData:JSON.stringify({ - grabbableKey:{ - grabbable:true - } - }) + grab: { grabbable: true } }); var sword2 = Entities.addEntity({ @@ -45,11 +41,7 @@ var sword2 = Entities.addEntity({ angularDamping: 0, damping: 0, script: SCRIPT_URL, - userData:JSON.stringify({ - grabbableKey:{ - grabbable:true - } - }) + grab: { grabbable: true } }); Script.scriptEnding.connect(function scriptEnding() { diff --git a/scripts/tutorials/createTetherballStick.js b/scripts/tutorials/createTetherballStick.js index 1b5bc36932..b940762137 100644 --- a/scripts/tutorials/createTetherballStick.js +++ b/scripts/tutorials/createTetherballStick.js @@ -1,6 +1,6 @@ "use strict"; /* jslint vars: true, plusplus: true, forin: true*/ -/* globals Tablet, Script, AvatarList, Users, Entities, MyAvatar, Camera, Overlays, Vec3, Quat, Controller, print, getControllerWorldLocation */ +/* globals Script, Entities, MyAvatar, Vec3, Quat */ /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ // // createTetherballStick.js @@ -59,11 +59,7 @@ var ballID = Entities.addEntity({ restitution: BALL_RESTITUTION, dynamic: true, collidesWith: "static,dynamic,otherAvatar,", - userData: JSON.stringify({ - grabbableKey: { - grabbable: false - } - }) + grab: { grabbable: false } }); var lineID = Entities.addEntity({ @@ -109,43 +105,38 @@ var STICK_PROPERTIES = { }, shapeType: 'box', lifetime: LIFETIME, - userData: JSON.stringify({ - grabbableKey: { - invertSolidWhileHeld: true, - ignoreIK: false + grab: { + grabbable: false, + grabFollowsController: false, + equippable: true, + equippableLeftPosition: { + x: -0.14998853206634521, + y: 0.17033983767032623, + z: 0.023199155926704407 }, - wearable: { - joints: { - RightHand: [{ - x: 0.15539926290512085, - y: 0.14493153989315033, - z: 0.023641478270292282 - }, { - x: 0.5481458902359009, - y: -0.4470711946487427, - z: -0.3148134648799896, - w: 0.6328644752502441 - }], - LeftHand: [{ - x: -0.14998853206634521, - y: 0.17033983767032623, - z: 0.023199155926704407 - }, - { - x: 0.6623835563659668, - y: -0.1671387255191803, - z: 0.7071226835250854, - w: 0.1823924481868744 - }] - } + equippableLeftRotation: { + x: 0.6623835563659668, + y: -0.1671387255191803, + z: 0.7071226835250854, + w: 0.1823924481868744 }, - ownerID: MyAvatar.sessionUUID, - ballID: ballID, - lineID: lineID, - actionID: actionID, - lifetime: LIFETIME, - maxDistanceBetweenBallAndStick: ACTION_DISTANCE * MAX_DISTANCE_MULTIPLIER - }) + equippableRightPosition: { + x: 0.15539926290512085, + y: 0.14493153989315033, + z: 0.023641478270292282 + }, + equippableRightRotation: { + x: 0.5481458902359009, + y: -0.4470711946487427, + z: -0.3148134648799896, + w: 0.6328644752502441 + } + }, + ownerID: MyAvatar.sessionUUID, + ballID: ballID, + lineID: lineID, + actionID: actionID, + maxDistanceBetweenBallAndStick: ACTION_DISTANCE * MAX_DISTANCE_MULTIPLIER }; Entities.addEntity(STICK_PROPERTIES); diff --git a/scripts/tutorials/entity_scripts/ambientSound.js b/scripts/tutorials/entity_scripts/ambientSound.js index dd964fb4e0..eab268f9bb 100644 --- a/scripts/tutorials/entity_scripts/ambientSound.js +++ b/scripts/tutorials/entity_scripts/ambientSound.js @@ -32,7 +32,7 @@ range: DEFAULT_RANGE, maxVolume: DEFAULT_VOLUME, disabled: true, - grabbableKey: { wantsTrigger: true }, + grab: { triggerable: true } }; var soundURL = ""; @@ -182,7 +182,7 @@ entity = entityID; _this = this; - var props = Entities.getEntityProperties(entity, [ "userData" ]); + var props = Entities.getEntityProperties(entity, [ "userData", "grab.triggerable" ]); var data = {}; if (props.userData) { data = JSON.parse(props.userData); @@ -194,14 +194,15 @@ changed = true; } } - if (!data.grabbableKey.wantsTrigger) { - data.grabbableKey.wantsTrigger = true; - changed = true; - } if (changed) { debugPrint("applying default values to userData"); Entities.editEntity(entity, { userData: JSON.stringify(data) }); } + + if (!props.grab.triggerable) { + Entities.editEntity(entity, { grab: { triggerable: true } }); + } + this._updateColor(data.disabled); this.updateSettings(); diff --git a/scripts/tutorials/entity_scripts/magneticBlock.js b/scripts/tutorials/entity_scripts/magneticBlock.js index 1ec5f2a6c6..2f073a5f41 100644 --- a/scripts/tutorials/entity_scripts/magneticBlock.js +++ b/scripts/tutorials/entity_scripts/magneticBlock.js @@ -52,32 +52,8 @@ It will behave as the constructor */ preload: function(id) { - /* - We will now override any existing userdata with the grabbable property. - Only retrieving userData - */ - var entityProperties = Entities.getEntityProperties(id, ['userData']); - var userData = { - grabbableKey: {} - }; - // Check if existing userData field exists. - if (entityProperties.userData && entityProperties.userData.length > 0) { - try { - userData = JSON.parse(entityProperties.userData); - if (!userData.grabbableKey) { - userData.grabbableKey = {}; // If by random change there is no grabbableKey in the userData. - } - } catch (e) { - // if user data is not valid json, we will simply overwrite it. - } - } - // Object must be triggerable inorder to bind releaseGrabEvent - userData.grabbableKey.grabbable = true; - // Apply the new properties to entity of id - Entities.editEntity(id, { - userData: JSON.stringify(userData) - }); + Entities.editEntity(id, { grab: { grabbable: true } }); Script.scriptEnding.connect(function() { Script.removeEventHandler(id, "releaseGrab", this.releaseGrab); }); diff --git a/scripts/tutorials/entity_scripts/springHold.js b/scripts/tutorials/entity_scripts/springHold.js index 059ea2cc6f..dbf0ceafeb 100644 --- a/scripts/tutorials/entity_scripts/springHold.js +++ b/scripts/tutorials/entity_scripts/springHold.js @@ -62,11 +62,7 @@ position: originalProps.position, shapeType: originalProps.shapeType, visible: true, - userData:JSON.stringify({ - grabbableKey:{ - grabbable:false - } - }) + grab: { grabbable: false } }; _this.copy = Entities.addEntity(props); } diff --git a/scripts/tutorials/entity_scripts/touch.js b/scripts/tutorials/entity_scripts/touch.js index 1d0586b350..6e1974a4fd 100644 --- a/scripts/tutorials/entity_scripts/touch.js +++ b/scripts/tutorials/entity_scripts/touch.js @@ -142,11 +142,7 @@ position: originalProps.position, shapeType: originalProps.shapeType, visible: true, - userData:JSON.stringify({ - grabbableKey:{ - grabbable:false - } - }) + grab: { grabbable: false } }; _this.copy = Entities.addEntity(props); } diff --git a/scripts/tutorials/makeBlocks.js b/scripts/tutorials/makeBlocks.js index 432f7444c4..c7d0425863 100644 --- a/scripts/tutorials/makeBlocks.js +++ b/scripts/tutorials/makeBlocks.js @@ -53,14 +53,10 @@ y: SIZE, z: SIZE }, - userData: JSON.stringify({ - grabbableKey: { - cloneable: true, - grabbable: true, - cloneLifetime: LIFETIME, - cloneLimit: 9999 - } - }), + grab: { grabbable: true }, + cloneable: true, + cloneLifetime: LIFETIME, + cloneLimit: 9999 position: Vec3.sum(MyAvatar.position, Vec3.sum(forwardOffset, forwardVector)), color: newColor(), script: SCRIPT_URL diff --git a/server-console/src/main.js b/server-console/src/main.js index c26938745b..5c7913d775 100644 --- a/server-console/src/main.js +++ b/server-console/src/main.js @@ -338,13 +338,15 @@ const HifiNotificationType = hfNotifications.NotificationType; var pendingNotifications = {} var notificationState = NotificationState.UNNOTIFIED; -function setNotificationState (notificationType, pending = true) { - pendingNotifications[notificationType] = pending; - notificationState = NotificationState.UNNOTIFIED; - for (var key in pendingNotifications) { - if (pendingNotifications[key]) { - notificationState = NotificationState.NOTIFIED; - break; +function setNotificationState (notificationType, pending = undefined) { + if (pending !== undefined) { + pendingNotifications[notificationType] = pending; + notificationState = NotificationState.UNNOTIFIED; + for (var key in pendingNotifications) { + if (pendingNotifications[key]) { + notificationState = NotificationState.NOTIFIED; + break; + } } } updateTrayMenu(homeServer ? homeServer.state : ProcessGroupStates.STOPPED); @@ -568,7 +570,42 @@ function updateLabels(serverState) { labels.people.icon = pendingNotifications[HifiNotificationType.PEOPLE] ? menuNotificationIcon : null; labels.wallet.icon = pendingNotifications[HifiNotificationType.WALLET] ? menuNotificationIcon : null; labels.marketplace.icon = pendingNotifications[HifiNotificationType.MARKETPLACE] ? menuNotificationIcon : null; - + var onlineUsers = trayNotifications.getOnlineUsers(); + delete labels.people.submenu; + if (onlineUsers) { + for (var name in onlineUsers) { + if(labels.people.submenu == undefined) { + labels.people.submenu = []; + } + labels.people.submenu.push({ + label: name, + enabled: (onlineUsers[name].location != undefined), + click: function (item) { + setNotificationState(HifiNotificationType.PEOPLE, false); + if(onlineUsers[item.label] && onlineUsers[item.label].location) { + StartInterface("hifi://" + onlineUsers[item.label].location.root.name + onlineUsers[item.label].location.path); + } + } + }); + } + } + var currentStories = trayNotifications.getCurrentStories(); + delete labels.goto.submenu; + if (currentStories) { + for (var location in currentStories) { + if(labels.goto.submenu == undefined) { + labels.goto.submenu = []; + } + labels.goto.submenu.push({ + label: "event in " + location, + location: location, + click: function (item) { + setNotificationState(HifiNotificationType.GOTO, false); + StartInterface("hifi://" + item.location + currentStories[item.location].path); + } + }); + } + } } function updateTrayMenu(serverState) { @@ -919,6 +956,8 @@ app.on('ready', function() { trayNotifications.startPolling(); } updateTrayMenu(ProcessGroupStates.STOPPED); - - maybeInstallDefaultContentSet(onContentLoaded); + + if (isServerInstalled()) { + maybeInstallDefaultContentSet(onContentLoaded); + } }); diff --git a/server-console/src/modules/hf-notifications.js b/server-console/src/modules/hf-notifications.js index 464d268c5e..b1f337bbc3 100644 --- a/server-console/src/modules/hf-notifications.js +++ b/server-console/src/modules/hf-notifications.js @@ -73,11 +73,17 @@ HifiNotification.prototype = { text = this.data + " of your connections are online." } message = "Click to open PEOPLE."; - url="hifiapp:PEOPLE" + url="hifiapp:PEOPLE"; } else { - text = this.data.username + " is available in " + this.data.location.root.name + "."; - message = "Click to join them."; - url="hifi://" + this.data.location.root.name + this.data.location.path; + if (this.data.location) { + text = this.data.username + " is available in " + this.data.location.root.name + "."; + message = "Click to join them."; + url="hifi://" + this.data.location.root.name + this.data.location.path; + } else { + text = this.data.username + " is online."; + message = "Click to open PEOPLE."; + url="hifiapp:PEOPLE"; + } } break; @@ -136,7 +142,8 @@ HifiNotification.prototype = { function HifiNotifications(config, menuNotificationCallback) { this.config = config; this.menuNotificationCallback = menuNotificationCallback; - this.onlineUsers = new Set([]); + this.onlineUsers = {}; + this.currentStories = {}; this.storiesSince = new Date(this.config.get("storiesNotifySince", "1970-01-01T00:00:00.000Z")); this.peopleSince = new Date(this.config.get("peopleNotifySince", "1970-01-01T00:00:00.000Z")); this.walletSince = new Date(this.config.get("walletNotifySince", "1970-01-01T00:00:00.000Z")); @@ -213,6 +220,12 @@ HifiNotifications.prototype = { clearInterval(this.marketplacePollTimer); } }, + getOnlineUsers: function () { + return this.onlineUsers; + }, + getCurrentStories: function () { + return this.currentStories; + }, _showNotification: function () { var _this = this; @@ -225,7 +238,7 @@ HifiNotifications.prototype = { // previous notification immediately when a // new one is submitted _this.pendingNotifications.shift(); - if(_this.pendingNotifications.length > 0) { + if (_this.pendingNotifications.length > 0) { _this._showNotification(); } }); @@ -289,7 +302,6 @@ HifiNotifications.prototype = { finished(false); return; } - console.log(content); if (!content.total_entries) { finished(true, token); return; @@ -313,7 +325,6 @@ HifiNotifications.prototype = { notifyData = content.data.updates; break; } - notifyData.forEach(function (notifyDataEntry) { _this._addNotification(new HifiNotification(notifyType, notifyDataEntry)); }); @@ -346,7 +357,7 @@ HifiNotifications.prototype = { 'include_actions=announcement', 'restriction=open,hifi', 'require_online=true', - 'per_page=1' + 'per_page=' + MAX_NOTIFICATION_ITEMS ]; var url = METAVERSE_SERVER_URL + STORIES_URL + '?' + options.join('&'); // call a second time to determine if there are no more stories and we should @@ -357,7 +368,34 @@ HifiNotifications.prototype = { 'bearer': token } }, function (error, data) { - _this._pollToDisableHighlight(NotificationType.GOTO, error, data); + if (error || !data.body) { + console.log("Error: unable to get " + url); + finished(false); + return; + } + var content = JSON.parse(data.body); + if (!content || content.status != 'success') { + console.log("Error: unable to get " + url); + finished(false); + return; + } + + if (!content.total_entries) { + finished(true, token); + return; + } + if (!content.total_entries) { + _this.menuNotificationCallback(NotificationType.GOTO, false); + } + _this.currentStories = {}; + content.user_stories.forEach(function (story) { + // only show a single instance of each story location + // in the menu. This may cause issues with domains + // where the story locations are significantly different + // for each story. + _this.currentStories[story.place_name] = story; + }); + _this.menuNotificationCallback(NotificationType.GOTO); }); } }); @@ -400,20 +438,19 @@ HifiNotifications.prototype = { console.log("Error: unable to get " + url); return false; } - console.log(content); if (!content.total_entries) { _this.menuNotificationCallback(NotificationType.PEOPLE, false); - _this.onlineUsers = new Set([]); + _this.onlineUsers = {}; return; } - var currentUsers = new Set([]); + var currentUsers = {}; var newUsers = new Set([]); content.data.users.forEach(function (user) { - currentUsers.add(user.username); - if (!_this.onlineUsers.has(user.username)) { + currentUsers[user.username] = user; + if (!(user.username in _this.onlineUsers)) { newUsers.add(user); - _this.onlineUsers.add(user.username); + _this.onlineUsers[user.username] = user; } }); _this.onlineUsers = currentUsers; diff --git a/tools/auto-tester/AppDataHighFidelity/Interface.json b/tools/auto-tester/AppDataHighFidelity/Interface.json new file mode 100644 index 0000000000..ca91a092c8 --- /dev/null +++ b/tools/auto-tester/AppDataHighFidelity/Interface.json @@ -0,0 +1,285 @@ +{ + "AddressManager/address": "hifi://localhost/1.07449e-05,0,-0.0174923/0,0,0,1", + "Audio/Desktop/INPUT": "Microphone (Realtek Audio)", + "Audio/Desktop/OUTPUT": "Speakers / Headphones (Realtek Audio)", + "Audio/VR/INPUT": "Microphone (Realtek Audio)", + "Audio/VR/OUTPUT": "Speakers / Headphones (Realtek Audio)", + "Avatar/Avatar/fullAvatarURL": "", + "Avatar/Debug Draw Animation": false, + "Avatar/Debug Draw Base of Support": false, + "Avatar/Debug Draw Default Pose": false, + "Avatar/Debug Draw Position": false, + "Avatar/Disable Eyelid Adjustment": false, + "Avatar/Draw Mesh": true, + "Avatar/Enable Default Motor Control": true, + "Avatar/Enable Inverse Kinematics": true, + "Avatar/Enable LookAt Snapping": true, + "Avatar/Enable Scripted Motor Control": true, + "Avatar/Face Tracking/Auto Mute Microphone": false, + "Avatar/Face Tracking/Binary Eyelid Control": true, + "Avatar/Face Tracking/Couple Eyelids": true, + "Avatar/Face Tracking/Mute Face Tracking": true, + "Avatar/Face Tracking/None": false, + "Avatar/Face Tracking/Use Audio for Mouth": true, + "Avatar/Face Tracking/Use Camera": true, + "Avatar/Face Tracking/Velocity Filter": true, + "Avatar/Fix Gaze (no saccade)": false, + "Avatar/Show Bounding Collision Shapes": false, + "Avatar/Show Detailed Collision": false, + "Avatar/Show IK Chains": false, + "Avatar/Show IK Constraints": false, + "Avatar/Show IK Targets": false, + "Avatar/Show My Eye Vectors": false, + "Avatar/Show Other Eye Vectors": false, + "Avatar/Show Receive Stats": false, + "Avatar/Show SensorToWorld Matrix": false, + "Avatar/Toggle Hips Following": false, + "Avatar/Turn using Head": false, + "Avatar/animGraphURL": "", + "Avatar/attachmentData/size": 0, + "Avatar/avatarEntityData/size": 0, + "Avatar/collisionSoundURL": "https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/Body_Hits_Impact.wav", + "Avatar/displayName": "", + "Avatar/dominantHand": "right", + "Avatar/flyingHMD": false, + "Avatar/fullAvatarModelName": "Default", + "Avatar/fullAvatarURL": "", + "Avatar/headPitch": 0, + "Avatar/pitchSpeed": 75, + "Avatar/scale": 1, + "Avatar/useSnapTurn": true, + "Avatar/userHeight": 1.7549999952316284, + "Avatar/yawSpeed": 100, + "Developer/Avatar/Debug Draw Animation": false, + "Developer/Avatar/Debug Draw Base of Support": false, + "Developer/Avatar/Debug Draw Default Pose": false, + "Developer/Avatar/Debug Draw Position": false, + "Developer/Avatar/Disable Eyelid Adjustment": false, + "Developer/Avatar/Draw Mesh": true, + "Developer/Avatar/Enable Default Motor Control": true, + "Developer/Avatar/Enable Inverse Kinematics": true, + "Developer/Avatar/Enable LookAt Snapping": true, + "Developer/Avatar/Enable Scripted Motor Control": true, + "Developer/Avatar/Face Tracking/Auto Mute Microphone": false, + "Developer/Avatar/Face Tracking/Binary Eyelid Control": true, + "Developer/Avatar/Face Tracking/Couple Eyelids": true, + "Developer/Avatar/Face Tracking/Mute Face Tracking": true, + "Developer/Avatar/Face Tracking/None": false, + "Developer/Avatar/Face Tracking/Use Audio for Mouth": true, + "Developer/Avatar/Face Tracking/Use Camera": true, + "Developer/Avatar/Face Tracking/Velocity Filter": true, + "Developer/Avatar/Fix Gaze (no saccade)": false, + "Developer/Avatar/Show Bounding Collision Shapes": false, + "Developer/Avatar/Show Detailed Collision": false, + "Developer/Avatar/Show IK Chains": false, + "Developer/Avatar/Show IK Constraints": false, + "Developer/Avatar/Show IK Targets": false, + "Developer/Avatar/Show My Eye Vectors": false, + "Developer/Avatar/Show Other Eye Vectors": false, + "Developer/Avatar/Show Receive Stats": false, + "Developer/Avatar/Show SensorToWorld Matrix": false, + "Developer/Avatar/Toggle Hips Following": false, + "Developer/Avatar/Turn using Head": false, + "Developer/Debug defaultScripts.js": false, + "Developer/Display Crash Options": true, + "Developer/Enable Speech Control API": false, + "Developer/Entities/Show Realtime Entity Stats": false, + "Developer/Hands/Show Hand Targets": false, + "Developer/Network/Disable Activity Logger": false, + "Developer/Network/Send wrong DS connect version": false, + "Developer/Network/Send wrong protocol version": false, + "Developer/Physics/Highlight Simulation Ownership": false, + "Developer/Physics/Show Bullet Bounding Boxes": false, + "Developer/Physics/Show Bullet Collision": false, + "Developer/Physics/Show Bullet Constraint Limits": false, + "Developer/Physics/Show Bullet Constraints": false, + "Developer/Physics/Show Bullet Contact Points": false, + "Developer/Picking/Force Coarse Picking": false, + "Developer/Render/Ambient Occlusion": false, + "Developer/Render/Compute Blendshapes": true, + "Developer/Render/Decimate Textures": false, + "Developer/Render/Default Skybox": true, + "Developer/Render/Enable Sparse Texture Management": true, + "Developer/Render/Maximum Texture Memory/1024 MB": false, + "Developer/Render/Maximum Texture Memory/2048 MB": false, + "Developer/Render/Maximum Texture Memory/256 MB": false, + "Developer/Render/Maximum Texture Memory/4 MB": false, + "Developer/Render/Maximum Texture Memory/4096 MB": false, + "Developer/Render/Maximum Texture Memory/512 MB": false, + "Developer/Render/Maximum Texture Memory/6144 MB": false, + "Developer/Render/Maximum Texture Memory/64 MB": false, + "Developer/Render/Maximum Texture Memory/8192 MB": false, + "Developer/Render/Maximum Texture Memory/Automatic Texture Memory": true, + "Developer/Render/OpenVR Threaded Submit": true, + "Developer/Render/Scale Resolution/1": true, + "Developer/Render/Scale Resolution/1/2": false, + "Developer/Render/Scale Resolution/1/3": false, + "Developer/Render/Scale Resolution/1/4": false, + "Developer/Render/Scale Resolution/2/3": false, + "Developer/Render/Shadows": true, + "Developer/Render/Temporal Antialiasing (FXAA if disabled)": true, + "Developer/Render/Throttle FPS If Not Focus": true, + "Developer/Render/World Axes": false, + "Developer/Show Animation Stats": false, + "Developer/Show Overlays": true, + "Developer/Show Statistics": false, + "Developer/Timing/Log Extra Timing Details": false, + "Developer/Timing/Log Render Pipeline Warnings": false, + "Developer/Timing/Performance Timer/Display Timing Details": false, + "Developer/Timing/Performance Timer/Expand /myAvatar": false, + "Developer/Timing/Performance Timer/Expand /myAvatar/simulation": false, + "Developer/Timing/Performance Timer/Expand /otherAvatar": false, + "Developer/Timing/Performance Timer/Expand /paintGL": false, + "Developer/Timing/Performance Timer/Expand /physics": false, + "Developer/Timing/Performance Timer/Expand /simulation": false, + "Developer/Timing/Performance Timer/Expand /update": false, + "Developer/Timing/Performance Timer/Only Display Top Ten": true, + "Developer/Timing/Show Timer": false, + "Developer/Timing/Suppress Timings Less than 10ms": false, + "Developer/UI/Desktop Tablet Becomes Toolbar": true, + "Developer/UI/HMD Tablet Becomes Toolbar": false, + "Developer/Verbose Logging": false, + "Display/3D TV - Interleaved": false, + "Display/3D TV - Side by Side Stereo": false, + "Display/Desktop": true, + "Display/Fullscreen": false, + "Display/Oculus Rift": false, + "Display/Oculus Rift (Simulator)": false, + "Edit/Allow Selecting of Large Models": true, + "Edit/Allow Selecting of Lights": true, + "Edit/Allow Selecting of Small Models": true, + "Edit/Auto Focus on Select": false, + "Edit/Create Entities As Grabbable (except Zones, Particles, and Lights)": true, + "Edit/Ease Orientation on Focus": false, + "Edit/Show Lights and Particle Systems in Create Mode": true, + "Edit/Show Zones in Create Mode": true, + "Entities/Show Realtime Entity Stats": false, + "Face Tracking/Auto Mute Microphone": false, + "Face Tracking/Binary Eyelid Control": true, + "Face Tracking/Couple Eyelids": true, + "Face Tracking/Mute Face Tracking": true, + "Face Tracking/None": false, + "Face Tracking/Use Audio for Mouth": true, + "Face Tracking/Use Camera": true, + "Face Tracking/Velocity Filter": true, + "Hands/Show Hand Targets": false, + "Leap Motion/desktopHeightOffset": 0.20000000298023224, + "Leap Motion/enabled": false, + "Leap Motion/sensorLocation": "Desktop", + "Maximum Texture Memory/1024 MB": false, + "Maximum Texture Memory/2048 MB": false, + "Maximum Texture Memory/256 MB": false, + "Maximum Texture Memory/4 MB": false, + "Maximum Texture Memory/4096 MB": false, + "Maximum Texture Memory/512 MB": false, + "Maximum Texture Memory/6144 MB": false, + "Maximum Texture Memory/64 MB": false, + "Maximum Texture Memory/8192 MB": false, + "Maximum Texture Memory/Automatic Texture Memory": true, + "Network/Disable Activity Logger": false, + "Network/Send wrong DS connect version": false, + "Network/Send wrong protocol version": false, + "Perception Neuron/enabled": false, + "Perception Neuron/serverAddress": "localhost", + "Perception Neuron/serverPort": 7001, + "Performance Timer/Display Timing Details": false, + "Performance Timer/Expand /myAvatar": false, + "Performance Timer/Expand /myAvatar/simulation": false, + "Performance Timer/Expand /otherAvatar": false, + "Performance Timer/Expand /paintGL": false, + "Performance Timer/Expand /physics": false, + "Performance Timer/Expand /simulation": false, + "Performance Timer/Expand /update": false, + "Performance Timer/Only Display Top Ten": true, + "Physics/Highlight Simulation Ownership": false, + "Physics/Show Bullet Bounding Boxes": false, + "Physics/Show Bullet Collision": false, + "Physics/Show Bullet Constraint Limits": false, + "Physics/Show Bullet Constraints": false, + "Physics/Show Bullet Contact Points": false, + "Picking/Force Coarse Picking": false, + "Render/Ambient Occlusion": false, + "Render/Compute Blendshapes": true, + "Render/Decimate Textures": false, + "Render/Default Skybox": true, + "Render/Enable Sparse Texture Management": true, + "Render/Maximum Texture Memory/1024 MB": false, + "Render/Maximum Texture Memory/2048 MB": false, + "Render/Maximum Texture Memory/256 MB": false, + "Render/Maximum Texture Memory/4 MB": false, + "Render/Maximum Texture Memory/4096 MB": false, + "Render/Maximum Texture Memory/512 MB": false, + "Render/Maximum Texture Memory/6144 MB": false, + "Render/Maximum Texture Memory/64 MB": false, + "Render/Maximum Texture Memory/8192 MB": false, + "Render/Maximum Texture Memory/Automatic Texture Memory": true, + "Render/OpenVR Threaded Submit": true, + "Render/Scale Resolution/1": true, + "Render/Scale Resolution/1/2": false, + "Render/Scale Resolution/1/3": false, + "Render/Scale Resolution/1/4": false, + "Render/Scale Resolution/2/3": false, + "Render/Shadows": true, + "Render/Temporal Antialiasing (FXAA if disabled)": true, + "Render/Throttle FPS If Not Focus": true, + "Render/World Axes": false, + "RunningScripts": [ + "file:///~//defaultScripts.js" + ], + "SDL2/enabled": true, + "Scale Resolution/1": true, + "Scale Resolution/1/2": false, + "Scale Resolution/1/3": false, + "Scale Resolution/1/4": false, + "Scale Resolution/2/3": false, + "Settings/Ask To Reset Settings on Start": false, + "Settings/Developer Menu": false, + "TabletSounds": "@Variant(\u0000\u0000\u0000\u000b\u0000\u0000\u0000\u0005\u0000\u0000\u0000(\u0000/\u0000s\u0000o\u0000u\u0000n\u0000d\u0000s\u0000/\u0000B\u0000u\u0000t\u0000t\u0000o\u0000n\u00000\u00006\u0000.\u0000w\u0000a\u0000v\u0000\u0000\u0000(\u0000/\u0000s\u0000o\u0000u\u0000n\u0000d\u0000s\u0000/\u0000B\u0000u\u0000t\u0000t\u0000o\u0000n\u00000\u00004\u0000.\u0000w\u0000a\u0000v\u0000\u0000\u0000(\u0000/\u0000s\u0000o\u0000u\u0000n\u0000d\u0000s\u0000/\u0000B\u0000u\u0000t\u0000t\u0000o\u0000n\u00000\u00007\u0000.\u0000w\u0000a\u0000v\u0000\u0000\u0000\"\u0000/\u0000s\u0000o\u0000u\u0000n\u0000d\u0000s\u0000/\u0000T\u0000a\u0000b\u00000\u00001\u0000.\u0000w\u0000a\u0000v\u0000\u0000\u0000\"\u0000/\u0000s\u0000o\u0000u\u0000n\u0000d\u0000s\u0000/\u0000T\u0000a\u0000b\u00000\u00002\u0000.\u0000w\u0000a\u0000v)", + "Timing/Log Extra Timing Details": false, + "Timing/Log Render Pipeline Warnings": false, + "Timing/Performance Timer/Display Timing Details": false, + "Timing/Performance Timer/Expand /myAvatar": false, + "Timing/Performance Timer/Expand /myAvatar/simulation": false, + "Timing/Performance Timer/Expand /otherAvatar": false, + "Timing/Performance Timer/Expand /paintGL": false, + "Timing/Performance Timer/Expand /physics": false, + "Timing/Performance Timer/Expand /simulation": false, + "Timing/Performance Timer/Expand /update": false, + "Timing/Performance Timer/Only Display Top Ten": true, + "Timing/Show Timer": false, + "Timing/Suppress Timings Less than 10ms": false, + "UI/Desktop Tablet Becomes Toolbar": true, + "UI/HMD Tablet Becomes Toolbar": false, + "UserActivityLoggerDisabled": false, + "View/Center Player In View": true, + "View/Enter First Person Mode in HMD": true, + "View/Entity Mode": false, + "View/First Person": true, + "View/Independent Mode": false, + "View/Mirror": false, + "View/Third Person": false, + "WindowGeometry": "@Rect(0 0 1920 1080)", + "WindowRoot.Windows/height": 706, + "WindowRoot.Windows/width": 480, + "WindowState": 0, + "activeDisplayPlugin": "Desktop", + "autoFocusOnSelect": true, + "cameraEaseOnFocus": true, + "desktopLODDecreaseFPS": 30.000001907348633, + "dynamicJitterBuffersEnabled": true, + "firstRun": false, + "hifi.ktx.cache_version": 1, + "hmdLODDecreaseFPS": 34, + "io.highfidelity.attachPoints": "{}", + "io.highfidelity.isEditing": false, + "loginDialogPoppedUp": false, + "sessionRunTime": 42, + "showLightsAndParticlesInEditMode": true, + "showZonesInEditMode": true, + "staticJitterBufferFrames": 1, + "toolbar/com.highfidelity.interface.toolbar.system/desktopHeight": 1059, + "toolbar/com.highfidelity.interface.toolbar.system/x": 655, + "toolbar/com.highfidelity.interface.toolbar.system/y": 953, + "toolbar/constrainToolbarToCenterX": true, + "wallet/autoLogout": true +} diff --git a/tools/auto-tester/AppDataHighFidelity/Interface/AccountInfo.bin b/tools/auto-tester/AppDataHighFidelity/Interface/AccountInfo.bin new file mode 100644 index 0000000000..65c971ea79 Binary files /dev/null and b/tools/auto-tester/AppDataHighFidelity/Interface/AccountInfo.bin differ diff --git a/tools/auto-tester/AppDataHighFidelity/Interface/avatarbookmarks.json b/tools/auto-tester/AppDataHighFidelity/Interface/avatarbookmarks.json new file mode 100644 index 0000000000..9976036f8e --- /dev/null +++ b/tools/auto-tester/AppDataHighFidelity/Interface/avatarbookmarks.json @@ -0,0 +1,861 @@ +{ + "Anime boy": { + "attachments": [ + ], + "avatarEntites": [ + { + "properties": { + "acceleration": { + "x": 0, + "y": 0, + "z": 0 + }, + "actionData": "", + "age": 6.915350914001465, + "ageAsText": "0 hours 0 minutes 6 seconds", + "angularDamping": 0.39346998929977417, + "angularVelocity": { + "x": 0, + "y": 0, + "z": 0 + }, + "animation": { + "allowTranslation": true, + "currentFrame": 0, + "firstFrame": 0, + "fps": 30, + "hold": false, + "lastFrame": 100000, + "loop": true, + "running": false, + "url": "" + }, + "boundingBox": { + "brn": { + "x": -0.10961885005235672, + "y": -0.19444090127944946, + "z": -0.15760529041290283 + }, + "center": { + "x": 2.6226043701171875e-06, + "y": -0.13999652862548828, + "z": -0.04999971389770508 + }, + "dimensions": { + "x": 0.21924294531345367, + "y": 0.10888873785734177, + "z": 0.2152111530303955 + }, + "tfl": { + "x": 0.10962409526109695, + "y": -0.0855521634221077, + "z": 0.057605862617492676 + } + }, + "canCastShadow": true, + "certificateID": "", + "clientOnly": true, + "cloneAvatarEntity": false, + "cloneDynamic": false, + "cloneLifetime": 300, + "cloneLimit": 0, + "cloneOriginID": "{00000000-0000-0000-0000-000000000000}", + "cloneable": false, + "collidesWith": "", + "collisionMask": 0, + "collisionSoundURL": "", + "collisionless": false, + "collisionsWillMove": false, + "compoundShapeURL": "", + "created": "2018-06-06T17:27:53Z", + "damping": 0.39346998929977417, + "density": 1000, + "description": "", + "dimensions": { + "x": 0.21924294531345367, + "y": 0.07768379896879196, + "z": 0.2055898904800415 + }, + "dynamic": false, + "editionNumber": 15, + "entityInstanceNumber": 0, + "friction": 0.5, + "gravity": { + "x": 0, + "y": 0, + "z": 0 + }, + "href": "", + "id": "{5d20c775-a0d7-4163-b158-4e0a784a4625}", + "ignoreForCollisions": false, + "itemArtist": "jyoum", + "itemCategories": "Wearables", + "itemDescription": "Wear these, and others will respect your authoritah.", + "itemLicense": "", + "itemName": "Aviators", + "jointRotations": [ + ], + "jointRotationsSet": [ + ], + "jointTranslations": [ + ], + "jointTranslationsSet": [ + ], + "lastEdited": 1528306178314655, + "lastEditedBy": "{439a2669-4626-487f-9dcf-2d15e77c69a2}", + "lifetime": -1, + "limitedRun": 4294967295, + "localPosition": { + "x": 2.6226043701171875e-06, + "y": -0.13999652862548828, + "z": -0.04999971389770508 + }, + "localRotation": { + "w": 0.9969173073768616, + "x": -0.07845909893512726, + "y": 0, + "z": 0 + }, + "locked": false, + "marketplaceID": "40d879ec-93f0-4b4a-8c58-dd6349bdb058", + "modelURL": "http://mpassets.highfidelity.com/40d879ec-93f0-4b4a-8c58-dd6349bdb058-v1/Aviator.fbx", + "name": "", + "naturalDimensions": { + "x": 0.1660931408405304, + "y": 0.05885136127471924, + "z": 0.15574991703033447 + }, + "naturalPosition": { + "x": 0, + "y": 1.6633577346801758, + "z": 0.048884183168411255 + }, + "originalTextures": "{\n \"aviator:Eyewear2F\": \"http://mpassets.highfidelity.com/40d879ec-93f0-4b4a-8c58-dd6349bdb058-v1/Aviator.fbx/Aviator.fbm/aviator_Eyewear_Diffuse.png\",\n \"aviator:Eyewear2F1\": \"http://mpassets.highfidelity.com/40d879ec-93f0-4b4a-8c58-dd6349bdb058-v1/Aviator.fbx/Aviator.fbm/aviator_Eyewear_Specular.png\"\n}\n", + "owningAvatarID": "{439a2669-4626-487f-9dcf-2d15e77c69a2}", + "parentID": "{439a2669-4626-487f-9dcf-2d15e77c69a2}", + "parentJointIndex": 66, + "position": { + "x": 2.6226043701171875e-06, + "y": -0.13999652862548828, + "z": -0.04999971389770508 + }, + "queryAACube": { + "scale": 0.9313028454780579, + "x": -1.4091639518737793, + "y": -10.133878707885742, + "z": 1.9983724355697632 + }, + "registrationPoint": { + "x": 0.5, + "y": 0.5, + "z": 0.5 + }, + "relayParentJoints": false, + "renderInfo": { + "drawCalls": 1, + "hasTransparent": false, + "texturesCount": 2, + "texturesSize": 1310720, + "verticesCount": 982 + }, + "restitution": 0.5, + "rotation": { + "w": 0.9969173073768616, + "x": -0.07845909893512726, + "y": 0, + "z": 0 + }, + "script": "", + "scriptTimestamp": 0, + "serverScripts": "", + "shapeType": "box", + "staticCertificateVersion": 0, + "textures": "", + "type": "Model", + "userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"HeadTop_End\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}", + "velocity": { + "x": 0, + "y": 0, + "z": 0 + }, + "visible": true + } + } + ], + "avatarScale": 1, + "avatarUrl": "http://mpassets.highfidelity.com/46e0fd52-3cff-462f-ba97-927338d88295-v1/AnimeBoy2.fst", + "version": 3 + }, + "Anime girl": { + "attachments": [ + ], + "avatarEntites": [ + { + "properties": { + "acceleration": { + "x": 0, + "y": 0, + "z": 0 + }, + "actionData": "", + "age": 19.66267967224121, + "ageAsText": "0 hours 0 minutes 19 seconds", + "angularDamping": 0.39346998929977417, + "angularVelocity": { + "x": 0, + "y": 0, + "z": 0 + }, + "animation": { + "allowTranslation": true, + "currentFrame": 0, + "firstFrame": 0, + "fps": 30, + "hold": false, + "lastFrame": 100000, + "loop": true, + "running": false, + "url": "" + }, + "boundingBox": { + "brn": { + "x": -0.10536206513643265, + "y": -0.16647332906723022, + "z": -0.12632352113723755 + }, + "center": { + "x": 0, + "y": -0.12999999523162842, + "z": -0.030000001192092896 + }, + "dimensions": { + "x": 0.2107241302728653, + "y": 0.07294666767120361, + "z": 0.1926470398902893 + }, + "tfl": { + "x": 0.10536206513643265, + "y": -0.09352666139602661, + "z": 0.06632351875305176 + } + }, + "canCastShadow": true, + "certificateID": "", + "clientOnly": true, + "cloneAvatarEntity": false, + "cloneDynamic": false, + "cloneLifetime": 300, + "cloneLimit": 0, + "cloneOriginID": "{00000000-0000-0000-0000-000000000000}", + "cloneable": false, + "collidesWith": "", + "collisionMask": 0, + "collisionSoundURL": "", + "collisionless": false, + "collisionsWillMove": false, + "compoundShapeURL": "", + "created": "2018-06-05T00:10:37Z", + "damping": 0.39346998929977417, + "density": 1000, + "description": "", + "dimensions": { + "x": 0.2107241302728653, + "y": 0.07294666767120361, + "z": 0.1926470398902893 + }, + "dynamic": false, + "editionNumber": 5, + "entityInstanceNumber": 0, + "friction": 0.5, + "gravity": { + "x": 0, + "y": 0, + "z": 0 + }, + "href": "", + "id": "{1586b83a-2af7-4532-9bfb-82fe3f5d5ce9}", + "ignoreForCollisions": false, + "itemArtist": "moam_00", + "itemCategories": "Wearables", + "itemDescription": "Perfect for side-glancin'.", + "itemLicense": "", + "itemName": "Blacker Fem Glasses", + "jointRotations": [ + ], + "jointRotationsSet": [ + ], + "jointTranslations": [ + ], + "jointTranslationsSet": [ + ], + "lastEdited": 1528157470041658, + "lastEditedBy": "{425df1a8-289b-42fc-819c-c3b2a12d7165}", + "lifetime": -1, + "limitedRun": 4294967295, + "localPosition": { + "x": 0, + "y": -0.12999999523162842, + "z": -0.029999999329447746 + }, + "localRotation": { + "w": 1, + "x": -2.2351741790771484e-08, + "y": 3.4924596548080444e-10, + "z": 3.725290298461914e-09 + }, + "locked": false, + "marketplaceID": "06781d12-9139-48f4-ac2a-417dde090981", + "modelURL": "http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx", + "name": "Female Glasses 3 by Mario Andrade", + "naturalDimensions": { + "x": 0.16209548711776733, + "y": 0.05611282214522362, + "z": 0.14819003641605377 + }, + "naturalPosition": { + "x": 0, + "y": -7.636845111846924e-08, + "z": 0 + }, + "originalTextures": "{\n \"file49\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Mixed_AO.jpg\",\n \"file81\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Metallic.jpg\",\n \"file84\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Roughness.jpg\",\n \"file86\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Base_Color.jpg\",\n \"file87\": \"http://mpassets.highfidelity.com/06781d12-9139-48f4-ac2a-417dde090981-v1/FemGlasses03.fbx/FemGlasses03.fbm/FemGlasses03Mat_Normal_DirectX.jpg\"\n}\n", + "owningAvatarID": "{1277f725-fbb4-478b-ae79-1241fd90e508}", + "parentID": "{1277f725-fbb4-478b-ae79-1241fd90e508}", + "parentJointIndex": 66, + "position": { + "x": 0, + "y": -0.12999999523162842, + "z": -0.029999999329447746 + }, + "queryAACube": { + "scale": 0.8840523958206177, + "x": -2.6587564945220947, + "y": -10.162277221679688, + "z": -0.9548344016075134 + }, + "registrationPoint": { + "x": 0.5, + "y": 0.5, + "z": 0.5 + }, + "relayParentJoints": false, + "renderInfo": { + "drawCalls": 1, + "hasTransparent": false, + "texturesCount": 5, + "texturesSize": 0, + "verticesCount": 1156 + }, + "restitution": 0.5, + "rotation": { + "w": 1, + "x": -2.2351741790771484e-08, + "y": 3.4924596548080444e-10, + "z": 3.725290298461914e-09 + }, + "script": "", + "scriptTimestamp": 0, + "serverScripts": "", + "shapeType": "box", + "staticCertificateVersion": 0, + "textures": "", + "type": "Model", + "userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"HeadTop_End\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}", + "velocity": { + "x": 0, + "y": 0, + "z": 0 + }, + "visible": true + } + } + ], + "avatarScale": 1, + "avatarUrl": "http://mpassets.highfidelity.com/0dce3426-55c8-4641-8dd5-d76eb575b64a-v1/Anime_F_Outfit.fst", + "version": 3 + }, + "Last Legends: Male": { + "attachments": [ + ], + "avatarEntites": [ + { + "properties": { + "acceleration": { + "blue": 0, + "green": 0, + "red": 0, + "x": 0, + "y": 0, + "z": 0 + }, + "actionData": "", + "age": 321.8835144042969, + "ageAsText": "0 hours 5 minutes 21 seconds", + "angularDamping": 0.39346998929977417, + "angularVelocity": { + "blue": 0, + "green": 0, + "red": 0, + "x": 0, + "y": 0, + "z": 0 + }, + "animation": { + "allowTranslation": true, + "currentFrame": 0, + "firstFrame": 0, + "fps": 30, + "hold": false, + "lastFrame": 100000, + "loop": true, + "running": false, + "url": "" + }, + "boundingBox": { + "brn": { + "blue": -0.03950843587517738, + "green": 0.20785385370254517, + "red": -0.04381325840950012, + "x": -0.04381325840950012, + "y": 0.20785385370254517, + "z": -0.03950843587517738 + }, + "center": { + "blue": 0, + "green": 0.23000000417232513, + "red": 0, + "x": 0, + "y": 0.23000000417232513, + "z": 0 + }, + "dimensions": { + "blue": 0.07901687175035477, + "green": 0.044292300939559937, + "red": 0.08762651681900024, + "x": 0.08762651681900024, + "y": 0.044292300939559937, + "z": 0.07901687175035477 + }, + "tfl": { + "blue": 0.03950843587517738, + "green": 0.2521461546421051, + "red": 0.04381325840950012, + "x": 0.04381325840950012, + "y": 0.2521461546421051, + "z": 0.03950843587517738 + } + }, + "canCastShadow": true, + "certificateID": "", + "clientOnly": true, + "cloneAvatarEntity": false, + "cloneDynamic": false, + "cloneLifetime": 300, + "cloneLimit": 0, + "cloneOriginID": "{00000000-0000-0000-0000-000000000000}", + "cloneable": false, + "collidesWith": "", + "collisionMask": 0, + "collisionSoundURL": "", + "collisionless": false, + "collisionsWillMove": false, + "compoundShapeURL": "", + "created": "2018-07-26T23:56:46Z", + "damping": 0.39346998929977417, + "density": 1000, + "description": "", + "dimensions": { + "blue": 0.07229919731616974, + "green": 0.06644226610660553, + "red": 0.03022606298327446, + "x": 0.03022606298327446, + "y": 0.06644226610660553, + "z": 0.07229919731616974 + }, + "dynamic": false, + "editionNumber": 58, + "entityInstanceNumber": 0, + "friction": 0.5, + "gravity": { + "blue": 0, + "green": 0, + "red": 0, + "x": 0, + "y": 0, + "z": 0 + }, + "href": "", + "id": "{03053239-bb37-4c51-a013-a1772baaeed5}", + "ignoreForCollisions": false, + "itemArtist": "jyoum", + "itemCategories": "Wearables", + "itemDescription": "A cool scifi watch for your avatar!", + "itemLicense": "", + "itemName": "Scifi Watch", + "jointRotations": [ + ], + "jointRotationsSet": [ + ], + "jointTranslations": [ + ], + "jointTranslationsSet": [ + ], + "lastEdited": 1532649569894305, + "lastEditedBy": "{042ac463-7879-40f0-8126-e2e56c4345ca}", + "lifetime": -1, + "limitedRun": 4294967295, + "localPosition": { + "blue": 0, + "green": 0.23000000417232513, + "red": 0, + "x": 0, + "y": 0.23000000417232513, + "z": 0 + }, + "localRotation": { + "w": 0.5910986065864563, + "x": -0.48726415634155273, + "y": -0.4088630974292755, + "z": 0.49599072337150574 + }, + "locked": false, + "marketplaceID": "0685794d-fddb-4bad-a608-6d7789ceda90", + "modelURL": "http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx", + "name": "Scifi Watch by Jimi", + "naturalDimensions": { + "blue": 0.055614765733480453, + "green": 0.0511094331741333, + "red": 0.023250818252563477, + "x": 0.023250818252563477, + "y": 0.0511094331741333, + "z": 0.055614765733480453 + }, + "naturalPosition": { + "blue": -0.06031447649002075, + "green": 1.4500460624694824, + "red": 0.6493338942527771, + "x": 0.6493338942527771, + "y": 1.4500460624694824, + "z": -0.06031447649002075 + }, + "originalTextures": "{\n \"file4\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Base_Color.png\",\n \"file5\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Normal_OpenGL.png\",\n \"file6\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Metallic.png\",\n \"file7\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Roughness.png\",\n \"file8\": \"http://mpassets.highfidelity.com/0685794d-fddb-4bad-a608-6d7789ceda90-v1/ScifiWatch.fbx/ScifiWatch/texture/lambert1_Emissive.png\"\n}\n", + "owningAvatarID": "{042ac463-7879-40f0-8126-e2e56c4345ca}", + "parentID": "{042ac463-7879-40f0-8126-e2e56c4345ca}", + "parentJointIndex": 16, + "position": { + "blue": 0, + "green": 0.23000000417232513, + "red": 0, + "x": 0, + "y": 0.23000000417232513, + "z": 0 + }, + "queryAACube": { + "scale": 0.3082179129123688, + "x": 495.7716979980469, + "y": 498.345703125, + "z": 498.52044677734375 + }, + "registrationPoint": { + "blue": 0.5, + "green": 0.5, + "red": 0.5, + "x": 0.5, + "y": 0.5, + "z": 0.5 + }, + "relayParentJoints": false, + "renderInfo": { + "drawCalls": 1, + "hasTransparent": false, + "texturesCount": 5, + "texturesSize": 786432, + "verticesCount": 273 + }, + "restitution": 0.5, + "rotation": { + "w": 0.5910986065864563, + "x": -0.48726415634155273, + "y": -0.4088630974292755, + "z": 0.49599072337150574 + }, + "script": "", + "scriptTimestamp": 0, + "serverScripts": "", + "shapeType": "box", + "staticCertificateVersion": 0, + "textures": "", + "type": "Model", + "userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"[LR]ForeArm\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}", + "velocity": { + "blue": 0, + "green": 0, + "red": 0, + "x": 0, + "y": 0, + "z": 0 + }, + "visible": true + } + }, + { + "properties": { + "acceleration": { + "blue": 0, + "green": 0, + "red": 0, + "x": 0, + "y": 0, + "z": 0 + }, + "actionData": "", + "age": 308.8044128417969, + "ageAsText": "0 hours 5 minutes 8 seconds", + "angularDamping": 0.39346998929977417, + "angularVelocity": { + "blue": 0, + "green": 0, + "red": 0, + "x": 0, + "y": 0, + "z": 0 + }, + "animation": { + "allowTranslation": true, + "currentFrame": 0, + "firstFrame": 0, + "fps": 30, + "hold": false, + "lastFrame": 100000, + "loop": true, + "running": false, + "url": "" + }, + "boundingBox": { + "brn": { + "blue": -0.2340194433927536, + "green": -0.07067721337080002, + "red": -0.17002610862255096, + "x": -0.17002610862255096, + "y": -0.07067721337080002, + "z": -0.2340194433927536 + }, + "center": { + "blue": -0.039825439453125, + "green": 0.02001953125, + "red": 0.0001678466796875, + "x": 0.0001678466796875, + "y": 0.02001953125, + "z": -0.039825439453125 + }, + "dimensions": { + "blue": 0.3883880078792572, + "green": 0.18139348924160004, + "red": 0.34038791060447693, + "x": 0.34038791060447693, + "y": 0.18139348924160004, + "z": 0.3883880078792572 + }, + "tfl": { + "blue": 0.1543685644865036, + "green": 0.11071627587080002, + "red": 0.17036180198192596, + "x": 0.17036180198192596, + "y": 0.11071627587080002, + "z": 0.1543685644865036 + } + }, + "canCastShadow": true, + "certificateID": "", + "clientOnly": true, + "cloneAvatarEntity": false, + "cloneDynamic": false, + "cloneLifetime": 300, + "cloneLimit": 0, + "cloneOriginID": "{00000000-0000-0000-0000-000000000000}", + "cloneable": false, + "collidesWith": "", + "collisionMask": 0, + "collisionSoundURL": "", + "collisionless": false, + "collisionsWillMove": false, + "compoundShapeURL": "", + "created": "2018-07-26T23:56:46Z", + "damping": 0.39346998929977417, + "density": 1000, + "description": "", + "dimensions": { + "blue": 0.38838762044906616, + "green": 0.16981728374958038, + "red": 0.33466479182243347, + "x": 0.33466479182243347, + "y": 0.16981728374958038, + "z": 0.38838762044906616 + }, + "dynamic": false, + "editionNumber": 18, + "entityInstanceNumber": 0, + "friction": 0.5, + "gravity": { + "blue": 0, + "green": 0, + "red": 0, + "x": 0, + "y": 0, + "z": 0 + }, + "href": "", + "id": "{1bf231ce-3913-4c53-be3c-b1f4094dac51}", + "ignoreForCollisions": false, + "itemArtist": "jyoum", + "itemCategories": "Wearables", + "itemDescription": "A stylish and classic piece of headwear for your avatar.", + "itemLicense": "", + "itemName": "Fedora", + "jointRotations": [ + ], + "jointRotationsSet": [ + ], + "jointTranslations": [ + ], + "jointTranslationsSet": [ + ], + "lastEdited": 1532649698129709, + "lastEditedBy": "{042ac463-7879-40f0-8126-e2e56c4345ca}", + "lifetime": -1, + "limitedRun": 4294967295, + "localPosition": { + "blue": -0.039825439453125, + "green": 0.02001953125, + "red": 0.0001678466796875, + "x": 0.0001678466796875, + "y": 0.02001953125, + "z": -0.039825439453125 + }, + "localRotation": { + "w": 0.9998477101325989, + "x": -9.898545982878204e-09, + "y": 5.670873406415922e-07, + "z": 0.017452405765652657 + }, + "locked": false, + "marketplaceID": "11c4208d-15d7-4449-9758-a08da6dbd3dc", + "modelURL": "http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx", + "name": "", + "naturalDimensions": { + "blue": 0.320981502532959, + "green": 0.14034485816955566, + "red": 0.2765824794769287, + "x": 0.2765824794769287, + "y": 0.14034485816955566, + "z": 0.320981502532959 + }, + "naturalPosition": { + "blue": 0.022502630949020386, + "green": 1.7460365295410156, + "red": 0.000143393874168396, + "x": 0.000143393874168396, + "y": 1.7460365295410156, + "z": 0.022502630949020386 + }, + "originalTextures": "{\n \"file5\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Base_Color.png\",\n \"file7\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Roughness.png\"\n}\n", + "owningAvatarID": "{042ac463-7879-40f0-8126-e2e56c4345ca}", + "parentID": "{042ac463-7879-40f0-8126-e2e56c4345ca}", + "parentJointIndex": 66, + "position": { + "blue": -0.039825439453125, + "green": 0.02001953125, + "red": 0.0001678466796875, + "x": 0.0001678466796875, + "y": 0.02001953125, + "z": -0.039825439453125 + }, + "queryAACube": { + "scale": 1.6202316284179688, + "x": 495.21051025390625, + "y": 498.5577697753906, + "z": 497.6370849609375 + }, + "registrationPoint": { + "blue": 0.5, + "green": 0.5, + "red": 0.5, + "x": 0.5, + "y": 0.5, + "z": 0.5 + }, + "relayParentJoints": false, + "renderInfo": { + "drawCalls": 1, + "hasTransparent": false, + "texturesCount": 2, + "texturesSize": 327680, + "verticesCount": 719 + }, + "restitution": 0.5, + "rotation": { + "w": 0.9998477101325989, + "x": -9.898545982878204e-09, + "y": 5.670873406415922e-07, + "z": 0.017452405765652657 + }, + "script": "", + "scriptTimestamp": 0, + "serverScripts": "", + "shapeType": "box", + "staticCertificateVersion": 0, + "textures": "", + "type": "Model", + "userData": "{\"Attachment\":{\"action\":\"attach\",\"joint\":\"HeadTop_End\",\"attached\":false,\"options\":{\"translation\":{\"x\":0,\"y\":0,\"z\":0},\"scale\":1}},\"grabbableKey\":{\"cloneable\":false,\"grabbable\":true}}", + "velocity": { + "blue": 0, + "green": 0, + "red": 0, + "x": 0, + "y": 0, + "z": 0 + }, + "visible": true + } + } + ], + "avatarScale": 1, + "avatarUrl": "http://mpassets.highfidelity.com/28569047-6f1a-4100-af67-8054ec397cc3-v1/LLMale2.fst", + "version": 3 + }, + "Last legends Female": { + "attachments": [ + ], + "avatarEntites": [ + ], + "avatarScale": 1, + "avatarUrl": "http://mpassets.highfidelity.com/8d823be5-6197-4418-b984-eb94160ed956-v1/LLFemale_Clothes.fst", + "version": 3 + }, + "Matthew": { + "attachments": [ + ], + "avatarEntites": [ + ], + "avatarScale": 1, + "avatarUrl": "http://mpassets.highfidelity.com/b652081b-a199-425e-ae5c-7815721bdc09-v1/matthew.fst", + "version": 3 + }, + "Priscilla": { + "attachments": [ + ], + "avatarEntites": [ + ], + "avatarScale": 1, + "avatarUrl": "http://mpassets.highfidelity.com/e7565f93-8bc5-47c2-b6eb-b3b31d4a1339-v1/priscilla.fst", + "version": 3 + }, + "Woody": { + "attachments": [ + ], + "avatarEntites": [ + ], + "avatarScale": 1, + "avatarUrl": "http://mpassets.highfidelity.com/ad348528-de38-420c-82bb-054cb22163f5-v1/mannequin.fst", + "version": 3 + } +} diff --git a/tools/auto-tester/AppDataHighFidelity/assignment-client/entities/models.json.gz b/tools/auto-tester/AppDataHighFidelity/assignment-client/entities/models.json.gz new file mode 100644 index 0000000000..1eeeac50ac Binary files /dev/null and b/tools/auto-tester/AppDataHighFidelity/assignment-client/entities/models.json.gz differ diff --git a/tools/auto-tester/AppDataHighFidelity/domain-server/AccountInfo.bin b/tools/auto-tester/AppDataHighFidelity/domain-server/AccountInfo.bin new file mode 100644 index 0000000000..645e34895e Binary files /dev/null and b/tools/auto-tester/AppDataHighFidelity/domain-server/AccountInfo.bin differ diff --git a/tools/auto-tester/AppDataHighFidelity/domain-server/config.json b/tools/auto-tester/AppDataHighFidelity/domain-server/config.json new file mode 100644 index 0000000000..d662d89f28 --- /dev/null +++ b/tools/auto-tester/AppDataHighFidelity/domain-server/config.json @@ -0,0 +1,7 @@ +{ + "metaverse": { + "automatic_networking": "full", + "id": "17b1cb9c-08c4-45aa-9257-163ad3913529" + }, + "version": 2.2 +} diff --git a/tools/auto-tester/AppDataHighFidelity/domain-server/entities/models.json.gz b/tools/auto-tester/AppDataHighFidelity/domain-server/entities/models.json.gz new file mode 100644 index 0000000000..1eeeac50ac Binary files /dev/null and b/tools/auto-tester/AppDataHighFidelity/domain-server/entities/models.json.gz differ diff --git a/tools/auto-tester/CMakeLists.txt b/tools/auto-tester/CMakeLists.txt index 1546a35f4c..06930ab0fe 100644 --- a/tools/auto-tester/CMakeLists.txt +++ b/tools/auto-tester/CMakeLists.txt @@ -48,4 +48,19 @@ if (WIN32) POST_BUILD COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} ${EXTRA_DEPLOY_OPTIONS} $<$,$,$>:--release> \"$\"" ) + + # add a custom command to copy the empty Apps/Data High Fidelity folder (i.e. - a valid folder with no entities) + add_custom_command( + TARGET ${TARGET_NAME} + POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/AppDataHighFidelity" "$/AppDataHighFidelity" + ) + + # add a custom command to copy the SSL DLLs + add_custom_command( + TARGET ${TARGET_NAME} + POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory D:/GitHub/vcpkg/installed/x64-windows/bin "$" + ) + endif () \ No newline at end of file diff --git a/tools/auto-tester/Create.PNG b/tools/auto-tester/Create.PNG index d82d4873a2..85d70e59ed 100644 Binary files a/tools/auto-tester/Create.PNG and b/tools/auto-tester/Create.PNG differ diff --git a/tools/auto-tester/Evaluate.PNG b/tools/auto-tester/Evaluate.PNG index d530dec994..2f711f61a3 100644 Binary files a/tools/auto-tester/Evaluate.PNG and b/tools/auto-tester/Evaluate.PNG differ diff --git a/tools/auto-tester/README.md b/tools/auto-tester/README.md index 0924e77f8b..e029955edc 100644 --- a/tools/auto-tester/README.md +++ b/tools/auto-tester/README.md @@ -5,14 +5,16 @@ The auto-tester is a stand alone application that provides a mechanism for regre * The snapshots are compared to a 'canonical' set of images that have been produced beforehand. * The result, if any test failed, is a zipped folder describing the failure. -Auto-tester has 4 functions, separated into 4 tabs: +Auto-tester has 5 functions, separated into 4 tabs: 1. Creating tests, MD files and recursive scripts -2. Evaluating the results of running tests -3. TestRail interface -4. Windows task bar utility (Windows only) +1. Windows task bar utility (Windows only) +1. Running tests +1. Evaluating the results of running tests +1. Web interface + ## Installation ### Executable -1. Download the installer by browsing to [here](). +1. Download the installer by browsing to [here](). 2. Double click on the installer and install to a convenient location ![](./setup_7z.PNG) 3. To run the auto-tester, double click **auto-tester.exe**. @@ -25,6 +27,17 @@ Python 3 can be downloaded from: 3. Mac (**macOS 64-bit/32-bit installer** or **macOS 64-bit/32-bit installer**) After installation - create an environment variable called PYTHON_PATH and set it to the folder containing the Python executable. +### AWS interface +#### Windows +1. Download the AWS CLI from `https://aws.amazon.com/cli/` +1. Install (installer is named `AWSCLI64PY3.msi`) +1. Open a new command prompt and run `aws configure` +1. Enter the AWS account number +1. Enter the secret key +1. Leave region name and ouput format as default [None] + +1. Install the latest release of Boto3 via pip: +>pip install boto3 # Create ![](./Create.PNG) @@ -36,6 +49,9 @@ This function is used to create/update Expected Images after a successful run of The user will be asked for the snapshot folder and then the tests root folder. All snapshots located in the snapshot folder will be used to create or update the expected images in the relevant tests. ### Details As an example - if the snapshots folder contains an image named `tests.content.entity.zone.zoneOrientation.00003.png`, then this file will be copied to `tests/contente/enity/zone/zoneOrientation/ExpectedImage0003.png`. +## Create Tests Outline +### Usage +This function creates an MD file in the (user-selected) tests root folder. The file provides links to both the tests and the MD files. ## Create MD file ### Usage This function creates a file named `test.md` from a `test.js` script. The user will be asked for the folder containing the test script: @@ -54,13 +70,20 @@ The process to produce the MD file is a simplistic parse of the test script. ### Usage This function creates all MD files recursively from the user-selected root folder. This can be any folder in the tests hierarchy (e.g. all engine\material tests). -The file provides a hierarchial list of all the tests -## Create Tests Outline +The file provides a hierarchal list of all the tests +## Create testAuto script ### Usage -This function creates an MD file in the (user-selected) tests root folder. The file provides links to both the tests and the MD files. +This function creates a script named `testAuto.js` in a user-selected test folder. +### Details +The script created runs the `test.js` script in the folder in automatic mode. The script is the same for all tests. +## Create all testAuto scripts +### Usage +This function creates all testAuto scripts recursively from the user-selected root folder. This can be any folder in the tests hierarchy (e.g. all engine\material tests). + +The file provides a hierarchical list of all the tests ## Create Recursive Script ### Usage -After the user selects a folder within the tests hierarchy, a script is created, named `testRecursive.js`. This script calls all `test.js` scripts in the subfolders. +After the user selects a folder within the tests hierarchy, a script is created, named `testRecursive.js`. This script calls all `test.js` scripts in the sub-folders. ### Details The various scripts are called in alphabetical order. @@ -92,6 +115,34 @@ autoTester.runRecursive(); In this case all recursive scripts, from the selected folder down, are created. Running this function in the tests root folder will create (or update) all the recursive scripts. +# Windows +![](./Windows.PNG) + +This tab is Windows-specific. It provides buttons to hide and show the task bar. + +The task bar should be hidden for all tests that use the primary camera. This is required to ensure that the snapshots are the right size. +# Run +![](./Run.PNG) +The run tab is used to run tests in automatic mode. The tests require the location of a folder to store files in; this folder can safely be re-used for any number of runs (the "Working Folder"). +The test script that is run is `https://github.com/highfidelity/hifi_tests/blob/master/tests/testRecursive.js`. The user can use a different branch' or even repository, if required. +Tests can be run server-less, or with the local host. In the second case, the domain-server and assignment-clients are run before starting Interface. +The default is to run the latest build. The user can select a specific build or PR if desired. +Testing can be started immediately, or run on a schedule. + +A test run is performed in a number of steps: +1. If the latest run has been selected then the `dev-builds.xml` file is downloaded to identify the latest build. +1. The installer is then downloaded. +1. After downloading the High Fidelity application is installed. This requires that UAC be disabled! +1. Any instances of the server-console, assignment-client, domain-server or Interface are killed before running the tests. +1. Interface is run with the appropriate command line parameters. +1. The expected images are then downloaded from GitHub and compared to the actual images from the tests. + +The working folder will ultimately contain the following: +1. A folder named `High Fidelity`. This is where High Fidelity is installed. +1. A folder named `snapshots`. This folder contains the zipped results folders (one for each run). It also contains both the actual images and the expected images from the last run; note that these are deleted before running tests (All PNG files in the folder are deleted. In addition - a text file named `tests_completed.txt` is created at the end of the test script - this signals that Interface did not crash during the test run. +1. The `dev-builds.xml` file, if it was downloaded. +1. The HighFidelity installer. Note that this is always named `HighFidelity-Beta-latest-dev` so as not to store too many installers over time. +1. A log file describing the runs. This file is appended to after each run. # Evaluate ![](./Evaluate.PNG) @@ -103,13 +154,13 @@ If any tests have failed, then a zipped folder will be created in the snapshots ### Usage Before starting the evaluation, make sure the GitHub user and branch are set correctly. The user should not normally be changed, but the branch may need to be set to the appropriate RC. -After setting the checkbox as required and pressing Evaluate - the user will be asked for the snapshots folder. +After setting the check-box as required and pressing Evaluate - the user will be asked for the snapshots folder. ### Details Evaluation proceeds in a number of steps: 1. A folder is created to store any failures -1. The expecetd images are download from GitHub. They are named slightly differently from the snapshots (e.g. `tests.engine.render.effect.highlight.coverage.00000.png` and `tests.engine.render.effect.highlight.coverage.00000_EI.png`). +1. The expected images are download from GitHub. They are named slightly differently from the snapshots (e.g. `tests.engine.render.effect.highlight.coverage.00000.png` and `tests.engine.render.effect.highlight.coverage.00000_EI.png`). 1. The images are then pair-wise compared, using the SSIM algorithm. A fixed threshold is used to define a mismatch. @@ -119,8 +170,10 @@ Evaluation proceeds in a number of steps: 1. If not in interactive mode, or the user has defined the results as an error, an error is written into the error folder. The error itself is a folder with the 3 images and a small text file containing details. 1. At the end of the test, the folder is zipped and the original folder is deleted. If there are no errors then the zipped folder will be empty. -# TestRail -![](./TestRail.PNG) + +# Web Interface +![](./WebInterface.PNG) +This tab has two functions: updating the TestRail cases, runs and results, and creating web page reports that are stored on AWS. Before updating TestRail, make sure the GitHub user and branch are set correctly. The user should not normally be changed, but the branch may need to be set to the appropriate RC. @@ -129,9 +182,9 @@ Any access to TestRail will require the TestRail account (default is High Fideli ![](./TestRailSelector.PNG) - The default test rail user is shown, and can be changed as needed. -- The username is usually the user's email. +- The user-name is usually the user's email. - The Project ID defaults to 14 - Interface. -- The Suite ID defaults to 1147 - Renderong. +- The Suite ID defaults to 1147 - Rendering. - The TestRail page provides 3 functions for writing to TestRail. ## Create Test Cases ### Usage @@ -188,10 +241,7 @@ A number of Python scripts are created: - `getRuns.py` reads the release names from TestRail - `addRun` is the script that writes to TestRail. -In addition - a file containing all the releases will be created - `runs.txt` -# Windows -![](./Windows.PNG) - -This tab is Windows-specific. It provides buttons to hide and show the task bar. - -The task bar should be hidden for all tests that use the primary camera. This is required to ensure that the snapshots are the right size. \ No newline at end of file +In addition - a file containing all the releases will be created - `runs.txt`. +## Create Web Page +This function requests a zipped results folder and converts it to a web page. The page is created in a user-selecetd working folder. +If the `Update AWS` checkbox is checked then the page will also be copied to AWS, and the appropriate URL will be displayed in the window below the button. diff --git a/tools/auto-tester/Run.PNG b/tools/auto-tester/Run.PNG new file mode 100644 index 0000000000..29f81299c8 Binary files /dev/null and b/tools/auto-tester/Run.PNG differ diff --git a/tools/auto-tester/TestRail.PNG b/tools/auto-tester/TestRail.PNG deleted file mode 100644 index 042d0cf1cb..0000000000 Binary files a/tools/auto-tester/TestRail.PNG and /dev/null differ diff --git a/tools/auto-tester/Web Interface.PNG b/tools/auto-tester/Web Interface.PNG new file mode 100644 index 0000000000..d7216e3c18 Binary files /dev/null and b/tools/auto-tester/Web Interface.PNG differ diff --git a/tools/auto-tester/WebInterface.PNG b/tools/auto-tester/WebInterface.PNG new file mode 100644 index 0000000000..d9d3df3fc6 Binary files /dev/null and b/tools/auto-tester/WebInterface.PNG differ diff --git a/tools/auto-tester/Windows.PNG b/tools/auto-tester/Windows.PNG index bf7b76ba02..32e3bfd53e 100644 Binary files a/tools/auto-tester/Windows.PNG and b/tools/auto-tester/Windows.PNG differ diff --git a/tools/auto-tester/src/AWSInterface.cpp b/tools/auto-tester/src/AWSInterface.cpp new file mode 100644 index 0000000000..628db5329c --- /dev/null +++ b/tools/auto-tester/src/AWSInterface.cpp @@ -0,0 +1,413 @@ +// +// AWSInterface.cpp +// +// Created by Nissim Hadar on 3 Oct 2018. +// Copyright 2013 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 "AWSInterface.h" + +#include +#include +#include + +#include +#include + +AWSInterface::AWSInterface(QObject* parent) : QObject(parent) { + _pythonInterface = new PythonInterface(); + _pythonCommand = _pythonInterface->getPythonCommand(); +} + +void AWSInterface::createWebPageFromResults(const QString& testResults, + const QString& workingDirectory, + QCheckBox* updateAWSCheckBox, + QLineEdit* urlLineEdit) { + _testResults = testResults; + _workingDirectory = workingDirectory; + + _urlLineEdit = urlLineEdit; + _urlLineEdit->setEnabled(false); + + extractTestFailuresFromZippedFolder(); + createHTMLFile(); + + if (updateAWSCheckBox->isChecked()) { + updateAWS(); + } +} + +void AWSInterface::extractTestFailuresFromZippedFolder() { + // For a test results zip file called `D:/tt/TestResults--2018-10-02_16-54-11(9426)[DESKTOP-PMKNLSQ].zip` + // the folder will be called `TestResults--2018-10-02_16-54-11(9426)[DESKTOP-PMKNLSQ]` + // and, this folder will be in the workign directory + QStringList parts =_testResults.split('/'); + QString zipFolderName = _workingDirectory + "/" + parts[parts.length() - 1].split('.')[0]; + if (QDir(zipFolderName).exists()) { + QDir dir = zipFolderName; + dir.removeRecursively(); + } + + QDir().mkdir(_workingDirectory); + JlCompress::extractDir(_testResults, _workingDirectory); +} + +void AWSInterface::createHTMLFile() { + // For file named `D:/tt/snapshots/TestResults--2018-10-03_15-35-28(9433)[DESKTOP-PMKNLSQ].zip` + // - the HTML will be in a folder named `TestResults--2018-10-03_15-35-28(9433)[DESKTOP-PMKNLSQ]` + QStringList pathComponents = _testResults.split('/'); + QString filename = pathComponents[pathComponents.length() - 1]; + _resultsFolder = filename.left(filename.length() - 4); + + QString resultsPath = _workingDirectory + "/" + _resultsFolder + "/"; + QDir().mkdir(resultsPath); + _htmlFilename = resultsPath + HTML_FILENAME; + + QFile file(_htmlFilename); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), + "Could not create '" + _htmlFilename + "'"); + exit(-1); + } + + QTextStream stream(&file); + + startHTMLpage(stream); + writeHead(stream); + writeBody(stream); + finishHTMLpage(stream); + + file.close(); +} + +void AWSInterface::startHTMLpage(QTextStream& stream) { + stream << "\n"; + stream << "\n"; +} + +void AWSInterface::writeHead(QTextStream& stream) { + stream << "\t" << "\n"; + stream << "\t" << "\t" << "\n"; + stream << "\t" << "\n"; +} + +void AWSInterface::writeBody(QTextStream& stream) { + stream << "\t" << "\n"; + writeTitle(stream); + writeTable(stream); + stream << "\t" << "\n"; +} + +void AWSInterface::finishHTMLpage(QTextStream& stream) { + stream << "\n"; +} + +void AWSInterface::writeTitle(QTextStream& stream) { + // Separate relevant components from the results name + // The expected format is as follows: `D:/tt/snapshots/TestResults--2018-10-04_11-09-41(PR14128)[DESKTOP-PMKNLSQ].zip` + QStringList tokens = _testResults.split('/'); + + // date_buildorPR_hostName will be 2018-10-03_15-35-28(9433)[DESKTOP-PMKNLSQ] + QString date_buildorPR_hostName = tokens[tokens.length() - 1].split("--")[1].split(".")[0]; + + QString buildorPR = date_buildorPR_hostName.split('(')[1].split(')')[0]; + QString hostName = date_buildorPR_hostName.split('[')[1].split(']')[0]; + + QStringList dateList = date_buildorPR_hostName.split('(')[0].split('_')[0].split('-'); + QString year = dateList[0]; + QString month = dateList[1]; + QString day = dateList[2]; + + QStringList timeList = date_buildorPR_hostName.split('(')[0].split('_')[1].split('-'); + QString hour = timeList[0]; + QString minute = timeList[1]; + QString second = timeList[2]; + + const QString months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + + stream << "\t" << "\t" << "\n"; + stream << "\t" << "\t" << "

Failures for "; + stream << months[month.toInt() - 1] << " " << day << ", " << year << ", "; + stream << hour << ":" << minute << ":" << second << ", "; + + if (buildorPR.left(2) == "PR") { + stream << "PR " << buildorPR.right(buildorPR.length() - 2) << ", "; + } else { + stream << "build " << buildorPR << ", "; + } + + stream << "run on " << hostName << "

\n"; +} + +void AWSInterface::writeTable(QTextStream& stream) { + QString previousTestName{ "" }; + + // Loop over all entries in directory. This is done in stages, as the names are not in the order of the tests + // The first stage reads the directory names into a list + // The second stage renames the tests by removing everything up to "--tests." + // The third stage renames the directories + // The fourth and lasts stage creates the HTML entries + // + // Note that failures are processed first, then successes + QStringList originalNamesFailures; + QStringList originalNamesSuccesses; + QDirIterator it1(_workingDirectory.toStdString().c_str()); + while (it1.hasNext()) { + QString nextDirectory = it1.next(); + + // Skip `.` and `..` directories + if (nextDirectory.right(1) == ".") { + continue; + } + + // Only process failure folders + if (!nextDirectory.contains("--tests.")) { + continue; + } + + // Look at the filename at the end of the path + QStringList parts = nextDirectory.split('/'); + QString name = parts[parts.length() - 1]; + if (name.left(7) == "Failure") { + originalNamesFailures.append(nextDirectory); + } else { + originalNamesSuccesses.append(nextDirectory); + } + } + + QStringList newNamesFailures; + for (int i = 0; i < originalNamesFailures.length(); ++i) { + newNamesFailures.append(originalNamesFailures[i].split("--tests.")[1]); + } + + QStringList newNamesSuccesses; + for (int i = 0; i < originalNamesSuccesses.length(); ++i) { + newNamesSuccesses.append(originalNamesSuccesses[i].split("--tests.")[1]); + } + + _htmlFailuresFolder = _workingDirectory + "/" + _resultsFolder + "/" + FAILURES_FOLDER; + QDir().mkdir(_htmlFailuresFolder); + + _htmlSuccessesFolder = _workingDirectory + "/" + _resultsFolder + "/" + SUCCESSES_FOLDER; + QDir().mkdir(_htmlSuccessesFolder); + + for (int i = 0; i < newNamesFailures.length(); ++i) { + QDir().rename(originalNamesFailures[i], _htmlFailuresFolder + "/" + newNamesFailures[i]); + } + + for (int i = 0; i < newNamesSuccesses.length(); ++i) { + QDir().rename(originalNamesSuccesses[i], _htmlSuccessesFolder + "/" + newNamesSuccesses[i]); + } + + QDirIterator it2((_htmlFailuresFolder).toStdString().c_str()); + while (it2.hasNext()) { + QString nextDirectory = it2.next(); + + // Skip `.` and `..` directories, as well as the HTML directory + if (nextDirectory.right(1) == "." || nextDirectory.contains(QString("/") + _resultsFolder + "/TestResults--")) { + continue; + } + + QStringList pathComponents = nextDirectory.split('/'); + QString filename = pathComponents[pathComponents.length() - 1]; + int splitIndex = filename.lastIndexOf("."); + QString testName = filename.left(splitIndex).replace(".", " / "); + QString testNumber = filename.right(filename.length() - (splitIndex + 1)); + + // The failures are ordered lexicographically, so we know that we can rely on the testName changing to create a new table + if (testName != previousTestName) { + if (!previousTestName.isEmpty()) { + closeTable(stream); + } + + previousTestName = testName; + + stream << "\t\t

" << testName << "

\n"; + + openTable(stream); + } + + createEntry(testNumber.toInt(), filename, stream, true); + } + + closeTable(stream); + stream << "\t" << "\t" << "\n"; + stream << "\t" << "\t" << "

The following tests passed:

"; + + QDirIterator it3((_htmlSuccessesFolder).toStdString().c_str()); + while (it3.hasNext()) { + QString nextDirectory = it3.next(); + + // Skip `.` and `..` directories, as well as the HTML directory + if (nextDirectory.right(1) == "." || nextDirectory.contains(QString("/") + _resultsFolder + "/TestResults--")) { + continue; + } + + QStringList pathComponents = nextDirectory.split('/'); + QString filename = pathComponents[pathComponents.length() - 1]; + int splitIndex = filename.lastIndexOf("."); + QString testName = filename.left(splitIndex).replace(".", " / "); + QString testNumber = filename.right(filename.length() - (splitIndex + 1)); + + // The failures are ordered lexicographically, so we know that we can rely on the testName changing to create a new table + if (testName != previousTestName) { + if (!previousTestName.isEmpty()) { + closeTable(stream); + } + + previousTestName = testName; + + stream << "\t\t

" << testName << "

\n"; + + openTable(stream); + } + + createEntry(testNumber.toInt(), filename, stream, false); + } + + closeTable(stream); +} + +void AWSInterface::openTable(QTextStream& stream) { + stream << "\t\t\n"; + stream << "\t\t\t\n"; + stream << "\t\t\t\t\n"; + stream << "\t\t\t\t\n"; + stream << "\t\t\t\t\n"; + stream << "\t\t\t\t\n"; + stream << "\t\t\t\n"; +} + +void AWSInterface::closeTable(QTextStream& stream) { + stream << "\t\t

Test

Actual Image

Expected Image

Difference Image

\n"; +} + +void AWSInterface::createEntry(int index, const QString& testResult, QTextStream& stream, const bool isFailure) { + stream << "\t\t\t\n"; + stream << "\t\t\t\t

" << QString::number(index) << "

\n"; + + // For a test named `D:/t/fgadhcUDHSFaidsfh3478JJJFSDFIUSOEIrf/Failure_1--tests.engine.interaction.pick.collision.many.00000` + // we need `Failure_1--tests.engine.interaction.pick.collision.many.00000` + QStringList resultNameComponents = testResult.split('/'); + QString resultName = resultNameComponents[resultNameComponents.length() - 1]; + + QString folder; + bool differenceFileFound; + if (isFailure) { + folder = FAILURES_FOLDER; + differenceFileFound = QFile::exists(_htmlFailuresFolder + "/" + resultName + "/Difference Image.png"); + } else { + folder = SUCCESSES_FOLDER; + differenceFileFound = QFile::exists(_htmlSuccessesFolder + "/" + resultName + "/Difference Image.png"); + } + + + stream << "\t\t\t\t\n"; + stream << "\t\t\t\t\n"; + + if (differenceFileFound) { + stream << "\t\t\t\t\n"; + } else { + stream << "\t\t\t\t

No Image Found

\n"; + } + + stream << "\t\t\t\n"; +} + +void AWSInterface::updateAWS() { + QString filename = _workingDirectory + "/updateAWS.py"; + if (QFile::exists(filename)) { + QFile::remove(filename); + } + QFile file(filename); + + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), + "Could not create 'addTestCases.py'"); + exit(-1); + } + + QTextStream stream(&file); + + stream << "import boto3\n"; + stream << "s3 = boto3.resource('s3')\n\n"; + + QDirIterator it1(_htmlFailuresFolder.toStdString().c_str()); + while (it1.hasNext()) { + QString nextDirectory = it1.next(); + + // Skip `.` and `..` directories + if (nextDirectory.right(1) == ".") { + continue; + } + + // nextDirectory looks like `D:/t/TestResults--2018-10-02_16-54-11(9426)[DESKTOP-PMKNLSQ]/failures/engine.render.effect.bloom.00000` + // We need to concatenate the last 3 components, to get `TestResults--2018-10-02_16-54-11(9426)[DESKTOP-PMKNLSQ]/failures/engine.render.effect.bloom.00000` + QStringList parts = nextDirectory.split('/'); + QString filename = parts[parts.length() - 3] + "/" + parts[parts.length() - 2] + "/" + parts[parts.length() - 1]; + + stream << "data = open('" << _workingDirectory << "/" << filename << "/" << "Actual Image.png" << "', 'rb')\n"; + stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Actual Image.png" << "', Body=data)\n\n"; + + stream << "data = open('" << _workingDirectory << "/" << filename << "/" << "Expected Image.png" << "', 'rb')\n"; + stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Expected Image.png" << "', Body=data)\n\n"; + + if (QFile::exists(_htmlFailuresFolder + "/" + parts[parts.length() - 1] + "/Difference Image.png")) { + stream << "data = open('" << _workingDirectory << "/" << filename << "/" << "Difference Image.png" << "', 'rb')\n"; + stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Difference Image.png" << "', Body=data)\n\n"; + } + } + + QDirIterator it2(_htmlSuccessesFolder.toStdString().c_str()); + while (it2.hasNext()) { + QString nextDirectory = it2.next(); + + // Skip `.` and `..` directories + if (nextDirectory.right(1) == ".") { + continue; + } + + // nextDirectory looks like `D:/t/TestResults--2018-10-02_16-54-11(9426)[DESKTOP-PMKNLSQ]/successes/engine.render.effect.bloom.00000` + // We need to concatenate the last 3 components, to get `TestResults--2018-10-02_16-54-11(9426)[DESKTOP-PMKNLSQ]/successes/engine.render.effect.bloom.00000` + QStringList parts = nextDirectory.split('/'); + QString filename = parts[parts.length() - 3] + "/" + parts[parts.length() - 2] + "/" + parts[parts.length() - 1]; + + stream << "data = open('" << _workingDirectory << "/" << filename << "/" << "Actual Image.png" << "', 'rb')\n"; + stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Actual Image.png" << "', Body=data)\n\n"; + + stream << "data = open('" << _workingDirectory << "/" << filename << "/" << "Expected Image.png" << "', 'rb')\n"; + stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Expected Image.png" << "', Body=data)\n\n"; + + if (QFile::exists(_htmlSuccessesFolder + "/" + parts[parts.length() - 1] + "/Difference Image.png")) { + stream << "data = open('" << _workingDirectory << "/" << filename << "/" << "Difference Image.png" << "', 'rb')\n"; + stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << filename << "/" << "Difference Image.png" << "', Body=data)\n\n"; + } + } + + stream << "data = open('" << _workingDirectory << "/" << _resultsFolder << "/" << HTML_FILENAME << "', 'rb')\n"; + stream << "s3.Bucket('hifi-content').put_object(Bucket='" << AWS_BUCKET << "', Key='" << _resultsFolder << "/" + << HTML_FILENAME << "', Body=data, ContentType='text/html')\n"; + + file.close(); + + // Show user the URL + _urlLineEdit->setEnabled(true); + _urlLineEdit->setText(QString("https://") + AWS_BUCKET + ".s3.amazonaws.com/" + _resultsFolder + "/" + HTML_FILENAME); + _urlLineEdit->setCursorPosition(0); + + QProcess* process = new QProcess(); + + connect(process, &QProcess::started, this, [=]() { _busyWindow.exec(); }); + connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); + connect(process, static_cast(&QProcess::finished), this, + [=](int exitCode, QProcess::ExitStatus exitStatus) { _busyWindow.hide(); }); + + QStringList parameters = QStringList() << filename ; + process->start(_pythonCommand, parameters); +} diff --git a/tools/auto-tester/src/AWSInterface.h b/tools/auto-tester/src/AWSInterface.h new file mode 100644 index 0000000000..c5be5f35bb --- /dev/null +++ b/tools/auto-tester/src/AWSInterface.h @@ -0,0 +1,72 @@ +// +// AWSInterface.h +// +// Created by Nissim Hadar on 3 Oct 2018. +// Copyright 2013 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_AWSInterface_h +#define hifi_AWSInterface_h + +#include +#include +#include +#include + +#include "ui/BusyWindow.h" + +#include "PythonInterface.h" + +class AWSInterface : public QObject { + Q_OBJECT +public: + explicit AWSInterface(QObject* parent = 0); + + void createWebPageFromResults(const QString& testResults, + const QString& workingDirectory, + QCheckBox* updateAWSCheckBox, + QLineEdit* urlLineEdit); + + void extractTestFailuresFromZippedFolder(); + void createHTMLFile(); + + void startHTMLpage(QTextStream& stream); + void writeHead(QTextStream& stream); + void writeBody(QTextStream& stream); + void finishHTMLpage(QTextStream& stream); + + void writeTitle(QTextStream& stream); + void writeTable(QTextStream& stream); + void openTable(QTextStream& stream); + void closeTable(QTextStream& stream); + + void createEntry(int index, const QString& testResult, QTextStream& stream, const bool isFailure); + + void updateAWS(); + +private: + QString _testResults; + QString _workingDirectory; + QString _resultsFolder; + QString _htmlFailuresFolder; + QString _htmlSuccessesFolder; + QString _htmlFilename; + + const QString FAILURES_FOLDER{ "failures" }; + const QString SUCCESSES_FOLDER{ "successes" }; + const QString HTML_FILENAME{ "TestResults.html" }; + + BusyWindow _busyWindow; + + PythonInterface* _pythonInterface; + QString _pythonCommand; + + QString AWS_BUCKET{ "hifi-qa" }; + + QLineEdit* _urlLineEdit; +}; + +#endif // hifi_AWSInterface_h \ No newline at end of file diff --git a/tools/auto-tester/src/Downloader.cpp b/tools/auto-tester/src/Downloader.cpp index 530a3b61bd..cb9863f34d 100644 --- a/tools/auto-tester/src/Downloader.cpp +++ b/tools/auto-tester/src/Downloader.cpp @@ -11,20 +11,20 @@ #include -Downloader::Downloader(QUrl imageUrl, QObject *parent) : QObject(parent) { +Downloader::Downloader(QUrl fileURL, QObject *parent) : QObject(parent) { connect( &_networkAccessManager, SIGNAL (finished(QNetworkReply*)), this, SLOT (fileDownloaded(QNetworkReply*)) ); - QNetworkRequest request(imageUrl); + QNetworkRequest request(fileURL); _networkAccessManager.get(request); } void Downloader::fileDownloaded(QNetworkReply* reply) { QNetworkReply::NetworkError error = reply->error(); if (error != QNetworkReply::NetworkError::NoError) { - QMessageBox::information(0, "Test Aborted", "Failed to download image: " + reply->errorString()); + QMessageBox::information(0, "Test Aborted", "Failed to download file: " + reply->errorString()); return; } diff --git a/tools/auto-tester/src/Downloader.h b/tools/auto-tester/src/Downloader.h index b0ad58fac5..6d1029698f 100644 --- a/tools/auto-tester/src/Downloader.h +++ b/tools/auto-tester/src/Downloader.h @@ -30,7 +30,7 @@ class Downloader : public QObject { Q_OBJECT public: - explicit Downloader(QUrl imageUrl, QObject *parent = 0); + explicit Downloader(QUrl fileURL, QObject *parent = 0); QByteArray downloadedData() const; diff --git a/tools/auto-tester/src/PythonInterface.cpp b/tools/auto-tester/src/PythonInterface.cpp new file mode 100644 index 0000000000..4922b8a8df --- /dev/null +++ b/tools/auto-tester/src/PythonInterface.cpp @@ -0,0 +1,32 @@ +// +// PythonInterface.cpp +// +// Created by Nissim Hadar on Oct 6, 2018. +// Copyright 2013 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 "PythonInterface.h" + +#include +#include +#include + +PythonInterface::PythonInterface() { + if (QProcessEnvironment::systemEnvironment().contains("PYTHON_PATH")) { + QString _pythonPath = QProcessEnvironment::systemEnvironment().value("PYTHON_PATH"); + if (!QFile::exists(_pythonPath + "/" + _pythonExe)) { + QMessageBox::critical(0, _pythonExe, QString("Python executable not found in ") + _pythonPath); + } + _pythonCommand = _pythonPath + "/" + _pythonExe; + } else { + QMessageBox::critical(0, "PYTHON_PATH not defined", + "Please set PYTHON_PATH to directory containing the Python executable"); + exit(-1); + } +} + +QString PythonInterface::getPythonCommand() { + return _pythonCommand; +} diff --git a/tools/auto-tester/src/PythonInterface.h b/tools/auto-tester/src/PythonInterface.h new file mode 100644 index 0000000000..f32a39a644 --- /dev/null +++ b/tools/auto-tester/src/PythonInterface.h @@ -0,0 +1,26 @@ +// +// PythonInterface.h +// +// Created by Nissim Hadar on Oct 6, 2018. +// Copyright 2013 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_PythonInterface_h +#define hifi_PythonInterface_h + +#include + +class PythonInterface { +public: + PythonInterface(); + + QString getPythonCommand(); + +private: + const QString _pythonExe{ "python.exe" }; + QString _pythonCommand; +}; + +#endif // hifi_PythonInterface_h diff --git a/tools/auto-tester/src/Test.cpp b/tools/auto-tester/src/Test.cpp index 3da789f405..582f6209af 100644 --- a/tools/auto-tester/src/Test.cpp +++ b/tools/auto-tester/src/Test.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -23,7 +24,10 @@ extern AutoTester* autoTester; #include -Test::Test() { +Test::Test(QProgressBar* progressBar, QCheckBox* checkBoxInteractiveMode) { + _progressBar = progressBar; + _checkBoxInteractiveMode = checkBoxInteractiveMode; + _mismatchWindow.setModal(true); if (autoTester) { @@ -34,17 +38,17 @@ Test::Test() { bool Test::createTestResultsFolderPath(const QString& directory) { QDateTime now = QDateTime::currentDateTime(); - _testResultsFolderPath = directory + "/" + TEST_RESULTS_FOLDER + "--" + now.toString(DATETIME_FORMAT); + _testResultsFolderPath = directory + "/" + TEST_RESULTS_FOLDER + "--" + now.toString(DATETIME_FORMAT) + "(local)[" + QHostInfo::localHostName() + "]"; QDir testResultsFolder(_testResultsFolderPath); // Create a new test results folder return QDir().mkdir(_testResultsFolderPath); } -void Test::zipAndDeleteTestResultsFolder() { +QString Test::zipAndDeleteTestResultsFolder() { QString zippedResultsFileName { _testResultsFolderPath + ".zip" }; QFileInfo fileInfo(zippedResultsFileName); - if (!fileInfo.exists()) { + if (fileInfo.exists()) { QFile::remove(zippedResultsFileName); } @@ -55,25 +59,30 @@ void Test::zipAndDeleteTestResultsFolder() { //In all cases, for the next evaluation _testResultsFolderPath = ""; - _index = 1; + _failureIndex = 1; + _successIndex = 1; + + return zippedResultsFileName; } -bool Test::compareImageLists(bool isInteractiveMode, QProgressBar* progressBar) { - progressBar->setMinimum(0); - progressBar->setMaximum(_expectedImagesFullFilenames.length() - 1); - progressBar->setValue(0); - progressBar->setVisible(true); +int Test::compareImageLists() { + _progressBar->setMinimum(0); + _progressBar->setMaximum(_expectedImagesFullFilenames.length() - 1); + _progressBar->setValue(0); + _progressBar->setVisible(true); // Loop over both lists and compare each pair of images // Quit loop if user has aborted due to a failed test. - bool success{ true }; bool keepOn{ true }; + int numberOfFailures{ 0 }; for (int i = 0; keepOn && i < _expectedImagesFullFilenames.length(); ++i) { // First check that images are the same size QImage resultImage(_resultImagesFullFilenames[i]); QImage expectedImage(_expectedImagesFullFilenames[i]); double similarityIndex; // in [-1.0 .. 1.0], where 1.0 means images are identical + + bool isInteractiveMode = (!_isRunningFromCommandLine && _checkBoxInteractiveMode->isChecked() && !_isRunningInAutomaticTestRun); // similarityIndex is set to -100.0 to indicate images are not the same size if (isInteractiveMode && (resultImage.width() != expectedImage.width() || resultImage.height() != expectedImage.height())) { @@ -83,19 +92,20 @@ bool Test::compareImageLists(bool isInteractiveMode, QProgressBar* progressBar) similarityIndex = _imageComparer.compareImages(resultImage, expectedImage); } - if (similarityIndex < THRESHOLD) { - TestFailure testFailure = TestFailure{ - (float)similarityIndex, - _expectedImagesFullFilenames[i].left(_expectedImagesFullFilenames[i].lastIndexOf("/") + 1), // path to the test (including trailing /) - QFileInfo(_expectedImagesFullFilenames[i].toStdString().c_str()).fileName(), // filename of expected image - QFileInfo(_resultImagesFullFilenames[i].toStdString().c_str()).fileName() // filename of result image - }; + TestResult testResult = TestResult{ + (float)similarityIndex, + _expectedImagesFullFilenames[i].left(_expectedImagesFullFilenames[i].lastIndexOf("/") + 1), // path to the test (including trailing /) + QFileInfo(_expectedImagesFullFilenames[i].toStdString().c_str()).fileName(), // filename of expected image + QFileInfo(_resultImagesFullFilenames[i].toStdString().c_str()).fileName() // filename of result image + }; - _mismatchWindow.setTestFailure(testFailure); + _mismatchWindow.setTestResult(testResult); + + if (similarityIndex < THRESHOLD) { + ++numberOfFailures; if (!isInteractiveMode) { - appendTestResultsToFile(_testResultsFolderPath, testFailure, _mismatchWindow.getComparisonImage()); - success = false; + appendTestResultsToFile(_testResultsFolderPath, testResult, _mismatchWindow.getComparisonImage(), true); } else { _mismatchWindow.exec(); @@ -103,41 +113,53 @@ bool Test::compareImageLists(bool isInteractiveMode, QProgressBar* progressBar) case USER_RESPONSE_PASS: break; case USE_RESPONSE_FAIL: - appendTestResultsToFile(_testResultsFolderPath, testFailure, _mismatchWindow.getComparisonImage()); - success = false; + appendTestResultsToFile(_testResultsFolderPath, testResult, _mismatchWindow.getComparisonImage(), true); break; case USER_RESPONSE_ABORT: keepOn = false; - success = false; break; default: assert(false); break; } } + } else { + appendTestResultsToFile(_testResultsFolderPath, testResult, _mismatchWindow.getComparisonImage(), false); } - progressBar->setValue(i); + _progressBar->setValue(i); } - progressBar->setVisible(false); - return success; + _progressBar->setVisible(false); + return numberOfFailures; } -void Test::appendTestResultsToFile(const QString& _testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage) { +void Test::appendTestResultsToFile(const QString& _testResultsFolderPath, TestResult testResult, QPixmap comparisonImage, bool hasFailed) { if (!QDir().exists(_testResultsFolderPath)) { QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Folder " + _testResultsFolderPath + " not found"); exit(-1); } - QString failureFolderPath { _testResultsFolderPath + "/Failure_" + QString::number(_index) + "--" + testFailure._actualImageFilename.left(testFailure._actualImageFilename.length() - 4) }; - if (!QDir().mkdir(failureFolderPath)) { - QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create folder " + failureFolderPath); + QString resultFolderPath; + if (hasFailed) { + resultFolderPath = _testResultsFolderPath + "/Failure_" + QString::number(_failureIndex) + "--" + + testResult._actualImageFilename.left(testResult._actualImageFilename.length() - 4); + + ++_failureIndex; + } else { + resultFolderPath = _testResultsFolderPath + "/Success_" + QString::number(_successIndex) + "--" + + testResult._actualImageFilename.left(testResult._actualImageFilename.length() - 4); + + ++_successIndex; + } + + if (!QDir().mkdir(resultFolderPath)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), + "Failed to create folder " + resultFolderPath); exit(-1); } - ++_index; - QFile descriptionFile(failureFolderPath + "/" + TEST_RESULTS_FILENAME); + QFile descriptionFile(resultFolderPath + "/" + TEST_RESULTS_FILENAME); if (!descriptionFile.open(QIODevice::ReadWrite)) { QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create file " + TEST_RESULTS_FILENAME); exit(-1); @@ -145,10 +167,10 @@ void Test::appendTestResultsToFile(const QString& _testResultsFolderPath, TestFa // Create text file describing the failure QTextStream stream(&descriptionFile); - stream << "Test failed in folder " << testFailure._pathname.left(testFailure._pathname.length() - 1) << endl; // remove trailing '/' - stream << "Expected image was " << testFailure._expectedImageFilename << endl; - stream << "Actual image was " << testFailure._actualImageFilename << endl; - stream << "Similarity _index was " << testFailure._error << endl; + stream << "Test failed in folder " << testResult._pathname.left(testResult._pathname.length() - 1) << endl; // remove trailing '/' + stream << "Expected image was " << testResult._expectedImageFilename << endl; + stream << "Actual image was " << testResult._actualImageFilename << endl; + stream << "Similarity index was " << testResult._error << endl; descriptionFile.close(); @@ -156,26 +178,34 @@ void Test::appendTestResultsToFile(const QString& _testResultsFolderPath, TestFa QString sourceFile; QString destinationFile; - sourceFile = testFailure._pathname + testFailure._expectedImageFilename; - destinationFile = failureFolderPath + "/" + "Expected Image.png"; + sourceFile = testResult._pathname + testResult._expectedImageFilename; + destinationFile = resultFolderPath + "/" + "Expected Image.png"; if (!QFile::copy(sourceFile, destinationFile)) { QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to copy " + sourceFile + " to " + destinationFile); exit(-1); } - sourceFile = testFailure._pathname + testFailure._actualImageFilename; - destinationFile = failureFolderPath + "/" + "Actual Image.png"; + sourceFile = testResult._pathname + testResult._actualImageFilename; + destinationFile = resultFolderPath + "/" + "Actual Image.png"; if (!QFile::copy(sourceFile, destinationFile)) { QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to copy " + sourceFile + " to " + destinationFile); exit(-1); } - comparisonImage.save(failureFolderPath + "/" + "Difference Image.png"); + comparisonImage.save(resultFolderPath + "/" + "Difference Image.png"); } -void Test::startTestsEvaluation(const QString& testFolder, const QString& branchFromCommandLine, const QString& userFromCommandLine) { - if (testFolder.isNull()) { - // Get list of JPEG images in folder, sorted by name +void Test::startTestsEvaluation(const bool isRunningFromCommandLine, + const bool isRunningInAutomaticTestRun, + const QString& snapshotDirectory, + const QString& branchFromCommandLine, + const QString& userFromCommandLine +) { + _isRunningFromCommandLine = isRunningFromCommandLine; + _isRunningInAutomaticTestRun = isRunningInAutomaticTestRun; + + if (snapshotDirectory.isNull()) { + // Get list of PNG images in folder, sorted by name QString previousSelection = _snapshotDirectory; QString parent = previousSelection.left(previousSelection.lastIndexOf('/')); if (!parent.isNull() && parent.right(1) != "/") { @@ -184,14 +214,14 @@ void Test::startTestsEvaluation(const QString& testFolder, const QString& branch _snapshotDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", parent, QFileDialog::ShowDirsOnly); - // If user cancelled then restore previous selection and return + // If user canceled then restore previous selection and return if (_snapshotDirectory == "") { _snapshotDirectory = previousSelection; return; } } else { - _snapshotDirectory = testFolder; - _exitWhenComplete = true; + _snapshotDirectory = snapshotDirectory; + _exitWhenComplete = (isRunningFromCommandLine && !isRunningInAutomaticTestRun); } // Quit if test results folder could not be created @@ -238,25 +268,28 @@ void Test::startTestsEvaluation(const QString& testFolder, const QString& branch } } - autoTester->downloadImages(expectedImagesURLs, _snapshotDirectory, _expectedImagesFilenames); + autoTester->downloadFiles(expectedImagesURLs, _snapshotDirectory, _expectedImagesFilenames, (void *)this); } - -void Test::finishTestsEvaluation(bool isRunningFromCommandline, bool interactiveMode, QProgressBar* progressBar) { - bool success = compareImageLists((!isRunningFromCommandline && interactiveMode), progressBar); +void Test::finishTestsEvaluation() { + int numberOfFailures = compareImageLists(); - if (interactiveMode && !isRunningFromCommandline) { - if (success) { + if (!_isRunningFromCommandLine && !_isRunningInAutomaticTestRun) { + if (numberOfFailures == 0) { QMessageBox::information(0, "Success", "All images are as expected"); } else { QMessageBox::information(0, "Failure", "One or more images are not as expected"); } } - zipAndDeleteTestResultsFolder(); + QString zippedFolderName = zipAndDeleteTestResultsFolder(); if (_exitWhenComplete) { exit(0); } + + if (_isRunningInAutomaticTestRun) { + autoTester->automaticTestRunEvaluationComplete(zippedFolderName, numberOfFailures); + } } bool Test::isAValidDirectory(const QString& pathname) { @@ -302,176 +335,8 @@ void Test::includeTest(QTextStream& textStream, const QString& testPathname) { textStream << "Script.include(testsRootPath + \"" << partialPathWithoutTests + "\");" << endl; } -// Creates a single script in a user-selected folder. -// This script will run all text.js scripts in every applicable sub-folder -void Test::createRecursiveScript() { - // Select folder to start recursing from - QString previousSelection = _testDirectory; - QString parent = previousSelection.left(previousSelection.lastIndexOf('/')); - if (!parent.isNull() && parent.right(1) != "/") { - parent += "/"; - } - - _testDirectory = - QFileDialog::getExistingDirectory(nullptr, "Please select folder that will contain the top level test script", parent, - QFileDialog::ShowDirsOnly); - - // If user cancelled then restore previous selection and return - if (_testDirectory == "") { - _testDirectory = previousSelection; - return; - } - - createRecursiveScript(_testDirectory, true); -} - -// This method creates a `testRecursive.js` script in every sub-folder. -void Test::createAllRecursiveScripts() { - // Select folder to start recursing from - QString previousSelection = _testsRootDirectory; - QString parent = previousSelection.left(previousSelection.lastIndexOf('/')); - if (!parent.isNull() && parent.right(1) != "/") { - parent += "/"; - } - - _testsRootDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the root folder for the recursive scripts", - parent, QFileDialog::ShowDirsOnly); - - // If user cancelled then restore previous selection and return - if (_testsRootDirectory == "") { - _testsRootDirectory = previousSelection; - return; - } - - createRecursiveScript(_testsRootDirectory, false); - - QDirIterator it(_testsRootDirectory.toStdString().c_str(), QDirIterator::Subdirectories); - while (it.hasNext()) { - QString directory = it.next(); - - // Only process directories - QDir dir; - if (!isAValidDirectory(directory)) { - continue; - } - - // Only process directories that have sub-directories - bool hasNoSubDirectories{ true }; - QDirIterator it2(directory.toStdString().c_str(), QDirIterator::Subdirectories); - while (it2.hasNext()) { - QString directory2 = it2.next(); - - // Only process directories - QDir dir; - if (isAValidDirectory(directory2)) { - hasNoSubDirectories = false; - break; - } - } - - if (!hasNoSubDirectories) { - createRecursiveScript(directory, false); - } - } - - QMessageBox::information(0, "Success", "Scripts have been created"); -} - -void Test::createRecursiveScript(const QString& topLevelDirectory, bool interactiveMode) { - const QString recursiveTestsFilename("testRecursive.js"); - QFile allTestsFilename(topLevelDirectory + "/" + recursiveTestsFilename); - if (!allTestsFilename.open(QIODevice::WriteOnly | QIODevice::Text)) { - QMessageBox::critical(0, - "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), - "Failed to create \"" + recursiveTestsFilename + "\" in directory \"" + topLevelDirectory + "\"" - ); - - exit(-1); - } - - QTextStream textStream(&allTestsFilename); - - const QString DATE_TIME_FORMAT("MMM d yyyy, h:mm"); - textStream << "// This is an automatically generated file, created by auto-tester on " << QDateTime::currentDateTime().toString(DATE_TIME_FORMAT) << endl << endl; - - // Include 'autoTest.js' - QString branch = autoTester->getSelectedBranch(); - QString user = autoTester->getSelectedUser(); - - textStream << "PATH_TO_THE_REPO_PATH_UTILS_FILE = \"https://raw.githubusercontent.com/" + user + "/hifi_tests/" + branch + "/tests/utils/branchUtils.js\";" << endl; - textStream << "Script.include(PATH_TO_THE_REPO_PATH_UTILS_FILE);" << endl; - textStream << "var autoTester = createAutoTester(Script.resolvePath(\".\"));" << endl << endl; - - textStream << "var testsRootPath = autoTester.getTestsRootPath();" << endl << endl; - - // Wait 10 seconds before starting - textStream << "if (typeof Test !== 'undefined') {" << endl; - textStream << " Test.wait(10000);" << endl; - textStream << "};" << endl << endl; - - textStream << "autoTester.enableRecursive();" << endl; - textStream << "autoTester.enableAuto();" << endl << endl; - - // This is used to verify that the recursive test contains at least one test - bool testFound{ false }; - - // Directories are included in reverse order. The autoTester scripts use a stack mechanism, - // so this ensures that the tests run in alphabetical order (a convenience when debugging) - QStringList directories; - - // First test if top-level folder has a test.js file - const QString testPathname{ topLevelDirectory + "/" + TEST_FILENAME }; - QFileInfo fileInfo(testPathname); - if (fileInfo.exists()) { - // Current folder contains a test - directories.push_front(testPathname); - - testFound = true; - } - - QDirIterator it(topLevelDirectory.toStdString().c_str(), QDirIterator::Subdirectories); - while (it.hasNext()) { - QString directory = it.next(); - - // Only process directories - QDir dir(directory); - if (!isAValidDirectory(directory)) { - continue; - } - - const QString testPathname { directory + "/" + TEST_FILENAME }; - QFileInfo fileInfo(testPathname); - if (fileInfo.exists()) { - // Current folder contains a test - directories.push_front(testPathname); - - testFound = true; - } - } - - if (interactiveMode && !testFound) { - QMessageBox::information(0, "Failure", "No \"" + TEST_FILENAME + "\" files found"); - allTestsFilename.close(); - return; - } - - // Now include the test scripts - for (int i = 0; i < directories.length(); ++i) { - includeTest(textStream, directories.at(i)); - } - - textStream << endl; - textStream << "autoTester.runRecursive();" << endl; - - allTestsFilename.close(); - - if (interactiveMode) { - QMessageBox::information(0, "Success", "Script has been created"); - } -} - void Test::createTests() { - // Rename files sequentially, as ExpectedResult_00000.jpeg, ExpectedResult_00001.jpg and so on + // Rename files sequentially, as ExpectedResult_00000.png, ExpectedResult_00001.png and so on // Any existing expected result images will be deleted QString previousSelection = _snapshotDirectory; QString parent = previousSelection.left(previousSelection.lastIndexOf('/')); @@ -482,7 +347,7 @@ void Test::createTests() { _snapshotDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", parent, QFileDialog::ShowDirsOnly); - // If user cancelled then restore previous selection and return + // If user canceled then restore previous selection and return if (_snapshotDirectory == "") { _snapshotDirectory = previousSelection; return; @@ -497,7 +362,7 @@ void Test::createTests() { _testsRootDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select test root folder", parent, QFileDialog::ShowDirsOnly); - // If user cancelled then restore previous selection and return + // If user canceled then restore previous selection and return if (_testsRootDirectory == "") { _testsRootDirectory = previousSelection; return; @@ -613,9 +478,7 @@ ExtractedText Test::getTestScriptLines(QString testFileName) { return relevantTextFromTest; } -// Create an MD file for a user-selected test. -// The folder selected must contain a script named "test.js", the file produced is named "test.md" -void Test::createMDFile() { +bool Test::createFileSetup() { // Folder selection QString previousSelection = _testDirectory; QString parent = previousSelection.left(previousSelection.lastIndexOf('/')); @@ -624,20 +487,18 @@ void Test::createMDFile() { } _testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test", parent, - QFileDialog::ShowDirsOnly); + QFileDialog::ShowDirsOnly); - // If user cancelled then restore previous selection and return + // If user canceled then restore previous selection and return if (_testDirectory == "") { _testDirectory = previousSelection; - return; + return false; } - createMDFile(_testDirectory); - - QMessageBox::information(0, "Success", "MD file has been created"); + return true; } -void Test::createAllMDFiles() { +bool Test::createAllFilesSetup() { // Select folder to start recursing from QString previousSelection = _testsRootDirectory; QString parent = previousSelection.left(previousSelection.lastIndexOf('/')); @@ -645,12 +506,32 @@ void Test::createAllMDFiles() { parent += "/"; } - _testsRootDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the root folder for the MD files", parent, - QFileDialog::ShowDirsOnly); + _testsRootDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the tests root folder", parent, + QFileDialog::ShowDirsOnly); // If user cancelled then restore previous selection and return if (_testsRootDirectory == "") { _testsRootDirectory = previousSelection; + return false; + } + + return true; +} + +// Create an MD file for a user-selected test. +// The folder selected must contain a script named "test.js", the file produced is named "test.md" +void Test::createMDFile() { + if (!createFileSetup()) { + return; + } + + if (createMDFile(_testDirectory)) { + QMessageBox::information(0, "Success", "MD file has been created"); + } +} + +void Test::createAllMDFiles() { + if (!createAllFilesSetup()) { return; } @@ -681,18 +562,18 @@ void Test::createAllMDFiles() { QMessageBox::information(0, "Success", "MD files have been created"); } -void Test::createMDFile(const QString& _testDirectory) { +bool Test::createMDFile(const QString& directory) { // Verify folder contains test.js file - QString testFileName(_testDirectory + "/" + TEST_FILENAME); + QString testFileName(directory + "/" + TEST_FILENAME); QFileInfo testFileInfo(testFileName); if (!testFileInfo.exists()) { QMessageBox::critical(0, "Error", "Could not find file: " + TEST_FILENAME); - return; + return false; } ExtractedText testScriptLines = getTestScriptLines(testFileName); - QString mdFilename(_testDirectory + "/" + "test.md"); + QString mdFilename(directory + "/" + "test.md"); QFile mdFile(mdFilename); if (!mdFile.open(QIODevice::WriteOnly)) { QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Failed to create file " + mdFilename); @@ -705,9 +586,6 @@ void Test::createMDFile(const QString& _testDirectory) { QString testName = testScriptLines.title; stream << "# " << testName << "\n"; - // Find the relevant part of the path to the test (i.e. from "tests" down - QString partialPath = extractPathFromTestsDown(_testDirectory); - stream << "## Run this script URL: [Manual](./test.js?raw=true) [Auto](./testAuto.js?raw=true)(from menu/Edit/Open and Run scripts from URL...)." << "\n\n"; stream << "## Preconditions" << "\n"; @@ -727,6 +605,223 @@ void Test::createMDFile(const QString& _testDirectory) { } mdFile.close(); + + foreach (auto test, testScriptLines.stepList) { + delete test; + } + testScriptLines.stepList.clear(); + + return true; +} + +void Test::createTestAutoScript() { + if (!createFileSetup()) { + return; + } + + if (createTestAutoScript(_testDirectory)) { + QMessageBox::information(0, "Success", "'autoTester.js` script has been created"); + } +} + +void Test::createAllTestAutoScripts() { + if (!createAllFilesSetup()) { + return; + } + + // First test if top-level folder has a test.js file + const QString testPathname{ _testsRootDirectory + "/" + TEST_FILENAME }; + QFileInfo fileInfo(testPathname); + if (fileInfo.exists()) { + createTestAutoScript(_testsRootDirectory); + } + + QDirIterator it(_testsRootDirectory.toStdString().c_str(), QDirIterator::Subdirectories); + while (it.hasNext()) { + QString directory = it.next(); + + // Only process directories + QDir dir; + if (!isAValidDirectory(directory)) { + continue; + } + + const QString testPathname{ directory + "/" + TEST_FILENAME }; + QFileInfo fileInfo(testPathname); + if (fileInfo.exists()) { + createTestAutoScript(directory); + } + } + + QMessageBox::information(0, "Success", "'autoTester.js' scripts have been created"); +} + +bool Test::createTestAutoScript(const QString& directory) { + // Verify folder contains test.js file + QString testFileName(directory + "/" + TEST_FILENAME); + QFileInfo testFileInfo(testFileName); + if (!testFileInfo.exists()) { + QMessageBox::critical(0, "Error", "Could not find file: " + TEST_FILENAME); + return false; + } + + QString testAutoScriptFilename(directory + "/" + "testAuto.js"); + QFile testAutoScriptFile(testAutoScriptFilename); + if (!testAutoScriptFile.open(QIODevice::WriteOnly)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), + "Failed to create file " + testAutoScriptFilename); + exit(-1); + } + + QTextStream stream(&testAutoScriptFile); + + stream << "if (typeof PATH_TO_THE_REPO_PATH_UTILS_FILE === 'undefined') PATH_TO_THE_REPO_PATH_UTILS_FILE = 'https://raw.githubusercontent.com/highfidelity/hifi_tests/master/tests/utils/branchUtils.js';\n"; + stream << "Script.include(PATH_TO_THE_REPO_PATH_UTILS_FILE);\n"; + stream << "var autoTester = createAutoTester(Script.resolvePath('.'));\n\n"; + stream << "autoTester.enableAuto();\n\n"; + stream << "Script.include('./test.js?raw=true');\n"; + + testAutoScriptFile.close(); + return true; +} + +// Creates a single script in a user-selected folder. +// This script will run all text.js scripts in every applicable sub-folder +void Test::createRecursiveScript() { + if (!createFileSetup()) { + return; + } + + createRecursiveScript(_testDirectory, true); + QMessageBox::information(0, "Success", "'testRecursive.js` script has been created"); +} + +// This method creates a `testRecursive.js` script in every sub-folder. +void Test::createAllRecursiveScripts() { + if (!createAllFilesSetup()) { + return; + } + + createRecursiveScript(_testsRootDirectory, false); + + QDirIterator it(_testsRootDirectory.toStdString().c_str(), QDirIterator::Subdirectories); + while (it.hasNext()) { + QString directory = it.next(); + + // Only process directories + QDir dir; + if (!isAValidDirectory(directory)) { + continue; + } + + // Only process directories that have sub-directories + bool hasNoSubDirectories{ true }; + QDirIterator it2(directory.toStdString().c_str(), QDirIterator::Subdirectories); + while (it2.hasNext()) { + QString directory2 = it2.next(); + + // Only process directories + QDir dir; + if (isAValidDirectory(directory2)) { + hasNoSubDirectories = false; + break; + } + } + + if (!hasNoSubDirectories) { + createRecursiveScript(directory, false); + } + } + + QMessageBox::information(0, "Success", "Scripts have been created"); +} + +void Test::createRecursiveScript(const QString& topLevelDirectory, bool interactiveMode) { + const QString recursiveTestsFilename("testRecursive.js"); + QFile allTestsFilename(topLevelDirectory + "/" + recursiveTestsFilename); + if (!allTestsFilename.open(QIODevice::WriteOnly | QIODevice::Text)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), + "Failed to create \"" + recursiveTestsFilename + "\" in directory \"" + topLevelDirectory + "\""); + + exit(-1); + } + + QTextStream textStream(&allTestsFilename); + + textStream << "// This is an automatically generated file, created by auto-tester" << endl; + + // Include 'autoTest.js' + QString branch = autoTester->getSelectedBranch(); + QString user = autoTester->getSelectedUser(); + + textStream << "PATH_TO_THE_REPO_PATH_UTILS_FILE = \"https://raw.githubusercontent.com/" + user + "/hifi_tests/" + branch + + "/tests/utils/branchUtils.js\";" + << endl; + textStream << "Script.include(PATH_TO_THE_REPO_PATH_UTILS_FILE);" << endl; + textStream << "var autoTester = createAutoTester(Script.resolvePath(\".\"));" << endl << endl; + + textStream << "var testsRootPath = autoTester.getTestsRootPath();" << endl << endl; + + // Wait 10 seconds before starting + textStream << "if (typeof Test !== 'undefined') {" << endl; + textStream << " Test.wait(10000);" << endl; + textStream << "};" << endl << endl; + + textStream << "autoTester.enableRecursive();" << endl; + textStream << "autoTester.enableAuto();" << endl << endl; + + // This is used to verify that the recursive test contains at least one test + bool testFound{ false }; + + // Directories are included in reverse order. The autoTester scripts use a stack mechanism, + // so this ensures that the tests run in alphabetical order (a convenience when debugging) + QStringList directories; + + // First test if top-level folder has a test.js file + const QString testPathname{ topLevelDirectory + "/" + TEST_FILENAME }; + QFileInfo fileInfo(testPathname); + if (fileInfo.exists()) { + // Current folder contains a test + directories.push_front(testPathname); + + testFound = true; + } + + QDirIterator it(topLevelDirectory.toStdString().c_str(), QDirIterator::Subdirectories); + while (it.hasNext()) { + QString directory = it.next(); + + // Only process directories + QDir dir(directory); + if (!isAValidDirectory(directory)) { + continue; + } + + const QString testPathname{ directory + "/" + TEST_FILENAME }; + QFileInfo fileInfo(testPathname); + if (fileInfo.exists()) { + // Current folder contains a test + directories.push_front(testPathname); + + testFound = true; + } + } + + if (interactiveMode && !testFound) { + QMessageBox::information(0, "Failure", "No \"" + TEST_FILENAME + "\" files found"); + allTestsFilename.close(); + return; + } + + // Now include the test scripts + for (int i = 0; i < directories.length(); ++i) { + includeTest(textStream, directories.at(i)); + } + + textStream << endl; + textStream << "autoTester.runRecursive();" << endl; + + allTestsFilename.close(); } void Test::createTestsOutline() { @@ -739,7 +834,7 @@ void Test::createTestsOutline() { _testDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select the tests root folder", parent, QFileDialog::ShowDirsOnly); - // If user cancelled then restore previous selection and return + // If user canceled then restore previous selection and return if (_testDirectory == "") { _testDirectory = previousSelection; return; @@ -949,3 +1044,19 @@ QString Test::getExpectedImagePartialSourceDirectory(const QString& filename) { void Test::setTestRailCreateMode(TestRailCreateMode testRailCreateMode) { _testRailCreateMode = testRailCreateMode; } + +void Test::createWebPage(QCheckBox* updateAWSCheckBox, QLineEdit* urlLineEdit) { + QString testResults = QFileDialog::getOpenFileName(nullptr, "Please select the zipped test results to update from", nullptr, + "Zipped Test Results (*.zip)"); + if (testResults.isNull()) { + return; + } + + QString tempDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select a folder to store temporary files in", + nullptr, QFileDialog::ShowDirsOnly); + if (tempDirectory.isNull()) { + return; + } + + _awsInterface.createWebPageFromResults(testResults, tempDirectory, updateAWSCheckBox, urlLineEdit); +} \ No newline at end of file diff --git a/tools/auto-tester/src/Test.h b/tools/auto-tester/src/Test.h index 853e9c98e2..f653a91782 100644 --- a/tools/auto-tester/src/Test.h +++ b/tools/auto-tester/src/Test.h @@ -16,6 +16,7 @@ #include #include +#include "AWSInterface.h" #include "ImageComparer.h" #include "ui/MismatchWindow.h" #include "TestRailInterface.h" @@ -41,28 +42,41 @@ enum TestRailCreateMode { class Test { public: - Test(); + Test(QProgressBar* progressBar, QCheckBox* checkBoxInteractiveMode); - void startTestsEvaluation(const QString& testFolder = QString(), const QString& branchFromCommandLine = QString(), const QString& userFromCommandLine = QString()); - void finishTestsEvaluation(bool isRunningFromCommandline, bool interactiveMode, QProgressBar* progressBar); + void startTestsEvaluation(const bool isRunningFromCommandLine, + const bool isRunningInAutomaticTestRun, + const QString& snapshotDirectory = QString(), + const QString& branchFromCommandLine = QString(), + const QString& userFromCommandLine = QString()); - void createRecursiveScript(); - void createAllRecursiveScripts(); - void createRecursiveScript(const QString& topLevelDirectory, bool interactiveMode); + void finishTestsEvaluation(); void createTests(); void createTestsOutline(); + bool createFileSetup(); + bool createAllFilesSetup(); + void createMDFile(); void createAllMDFiles(); - void createMDFile(const QString& topLevelDirectory); + bool createMDFile(const QString& directory); + + void createTestAutoScript(); + void createAllTestAutoScripts(); + bool createTestAutoScript(const QString& directory); void createTestRailTestCases(); void createTestRailRun(); + void updateTestRailRunResult(); - bool compareImageLists(bool isInteractiveMode, QProgressBar* progressBar); + void createRecursiveScript(); + void createAllRecursiveScripts(); + void createRecursiveScript(const QString& topLevelDirectory, bool interactiveMode); + + int compareImageLists(); QStringList createListOfAll_imagesInDirectory(const QString& imageFormat, const QString& pathToImageDirectory); @@ -70,10 +84,10 @@ public: void includeTest(QTextStream& textStream, const QString& testPathname); - void appendTestResultsToFile(const QString& testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage); + void appendTestResultsToFile(const QString& testResultsFolderPath, TestResult testResult, QPixmap comparisonImage, bool hasFailed); bool createTestResultsFolderPath(const QString& directory); - void zipAndDeleteTestResultsFolder(); + QString zipAndDeleteTestResultsFolder(); static bool isAValidDirectory(const QString& pathname); QString extractPathFromTestsDown(const QString& fullPath); @@ -84,12 +98,20 @@ public: void setTestRailCreateMode(TestRailCreateMode testRailCreateMode); + void createWebPage(QCheckBox* updateAWSCheckBox, QLineEdit* urlLineEdit); + private: + QProgressBar* _progressBar; + QCheckBox* _checkBoxInteractiveMode; + + bool _isRunningFromCommandLine{ false }; + bool _isRunningInAutomaticTestRun{ false }; + const QString TEST_FILENAME { "test.js" }; const QString TEST_RESULTS_FOLDER { "TestResults" }; const QString TEST_RESULTS_FILENAME { "TestResults.txt" }; - const double THRESHOLD{ 0.96 }; + const double THRESHOLD{ 0.935 }; QDir _imageDirectory; @@ -98,7 +120,8 @@ private: ImageComparer _imageComparer; QString _testResultsFolderPath; - int _index { 1 }; + int _failureIndex{ 1 }; + int _successIndex{ 1 }; // Expected images are in the format ExpectedImage_dddd.jpg (d == decimal digit) const int NUM_DIGITS { 5 }; @@ -132,8 +155,9 @@ private: bool _exitWhenComplete{ false }; TestRailInterface _testRailInterface; - TestRailCreateMode _testRailCreateMode { PYTHON }; + + AWSInterface _awsInterface; }; #endif // hifi_test_h \ No newline at end of file diff --git a/tools/auto-tester/src/TestRailInterface.cpp b/tools/auto-tester/src/TestRailInterface.cpp index 93559490e5..f943935539 100644 --- a/tools/auto-tester/src/TestRailInterface.cpp +++ b/tools/auto-tester/src/TestRailInterface.cpp @@ -16,66 +16,40 @@ #include #include +#include #include #include TestRailInterface::TestRailInterface() { _testRailTestCasesSelectorWindow.setURL("https://highfidelity.testrail.net"); - ////_testRailTestCasesSelectorWindow.setURL("https://nissimhadar.testrail.io"); _testRailTestCasesSelectorWindow.setUser("@highfidelity.io"); - ////_testRailTestCasesSelectorWindow.setUser("nissim.hadar@gmail.com"); - _testRailTestCasesSelectorWindow.setProjectID(INTERFACE_PROJECT_ID); - ////_testRailTestCasesSelectorWindow.setProjectID(2); + _testRailTestCasesSelectorWindow.setProjectID(INTERFACE_AUTOMATION_PROJECT_ID); _testRailTestCasesSelectorWindow.setSuiteID(INTERFACE_SUITE_ID); - ////_testRailTestCasesSelectorWindow.setSuiteID(2); _testRailRunSelectorWindow.setURL("https://highfidelity.testrail.net"); - ////_testRailRunSelectorWindow.setURL("https://nissimhadar.testrail.io"); _testRailRunSelectorWindow.setUser("@highfidelity.io"); - ////_testRailRunSelectorWindow.setUser("nissim.hadar@gmail.com"); - _testRailRunSelectorWindow.setProjectID(INTERFACE_PROJECT_ID); - ////_testRailRunSelectorWindow.setProjectID(2); + _testRailRunSelectorWindow.setProjectID(INTERFACE_AUTOMATION_PROJECT_ID); _testRailRunSelectorWindow.setSuiteID(INTERFACE_SUITE_ID); - ////_testRailRunSelectorWindow.setSuiteID(2); _testRailResultsSelectorWindow.setURL("https://highfidelity.testrail.net"); - ////_testRailResultsSelectorWindow.setURL("https://nissimhadar.testrail.io"); _testRailResultsSelectorWindow.setUser("@highfidelity.io"); - ////_testRailResultsSelectorWindow.setUser("nissim.hadar@gmail.com"); - _testRailResultsSelectorWindow.setProjectID(INTERFACE_PROJECT_ID); - ////_testRailResultsSelectorWindow.setProjectID(2); + _testRailResultsSelectorWindow.setProjectID(INTERFACE_AUTOMATION_PROJECT_ID); _testRailResultsSelectorWindow.setSuiteID(INTERFACE_SUITE_ID); - ////_testRailResultsSelectorWindow.setSuiteID(2); + + _pythonInterface = new PythonInterface(); + _pythonCommand = _pythonInterface->getPythonCommand(); } QString TestRailInterface::getObject(const QString& path) { return path.right(path.length() - path.lastIndexOf("/") - 1); } - -bool TestRailInterface::setPythonCommand() { - if (QProcessEnvironment::systemEnvironment().contains("PYTHON_PATH")) { - QString _pythonPath = QProcessEnvironment::systemEnvironment().value("PYTHON_PATH"); - if (!QFile::exists(_pythonPath + "/" + pythonExe)) { - QMessageBox::critical(0, pythonExe, QString("Python executable not found in ") + _pythonPath); - } - _pythonCommand = _pythonPath + "/" + pythonExe; - return true; - } else { - QMessageBox::critical(0, "PYTHON_PATH not defined", - "Please set PYTHON_PATH to directory containing the Python executable"); - return false; - } - - return false; -} - // Creates the testrail.py script // This is the file linked to from http://docs.gurock.com/testrail-api2/bindings-python void TestRailInterface::createTestRailDotPyScript() { @@ -240,7 +214,7 @@ bool TestRailInterface::requestTestRailTestCasesDataFromUser() { _url = _testRailTestCasesSelectorWindow.getURL() + "/"; _user = _testRailTestCasesSelectorWindow.getUser(); _password = _testRailTestCasesSelectorWindow.getPassword(); - ////_password = "tutKA76";//// + _projectID = QString::number(_testRailTestCasesSelectorWindow.getProjectID()); _suiteID = QString::number(_testRailTestCasesSelectorWindow.getSuiteID()); @@ -258,7 +232,7 @@ bool TestRailInterface::requestTestRailRunDataFromUser() { _url = _testRailRunSelectorWindow.getURL() + "/"; _user = _testRailRunSelectorWindow.getUser(); _password = _testRailRunSelectorWindow.getPassword(); - ////_password = "tutKA76";//// + _projectID = QString::number(_testRailRunSelectorWindow.getProjectID()); _suiteID = QString::number(_testRailRunSelectorWindow.getSuiteID()); @@ -276,7 +250,7 @@ bool TestRailInterface::requestTestRailResultsDataFromUser() { _url = _testRailResultsSelectorWindow.getURL() + "/"; _user = _testRailResultsSelectorWindow.getUser(); _password = _testRailResultsSelectorWindow.getPassword(); - ////_password = "tutKA76";//// + _projectID = QString::number(_testRailResultsSelectorWindow.getProjectID()); _suiteID = QString::number(_testRailResultsSelectorWindow.getSuiteID()); @@ -377,8 +351,9 @@ void TestRailInterface::createAddTestCasesPythonScript(const QString& testDirect QMessageBox::Yes | QMessageBox::No).exec() ) { QProcess* process = new QProcess(); - connect(process, &QProcess::started, this, [=]() { _busyWindow.exec(); }); + connect(process, &QProcess::started, this, [=]() { _busyWindow.exec(); }); + connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); connect(process, static_cast(&QProcess::finished), this, [=](int exitCode, QProcess::ExitStatus exitStatus) { _busyWindow.hide(); }); @@ -489,8 +464,8 @@ void TestRailInterface::addRun() { stream << "\tcase_ids.append(case['id'])\n\n"; // Now, we can create the run - stream << "data = { 'name': '" + _sectionNames[_testRailRunSelectorWindow.getSectionID()].replace("Section", "Run") + - "', 'suite_id': " + _suiteID + + stream << "data = { 'name': '" + _sectionNames[_testRailRunSelectorWindow.getSectionID()].replace("Section", "Run") + "[" + + QHostInfo::localHostName() + "]" + "', 'suite_id': " + _suiteID + ", 'include_all': False, 'case_ids': case_ids}\n"; stream << "run = client.send_post('add_run/" + _projectID + "', data)\n"; @@ -503,7 +478,7 @@ void TestRailInterface::addRun() { ) { QProcess* process = new QProcess(); connect(process, &QProcess::started, this, [=]() { _busyWindow.exec(); }); - + connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); connect(process, static_cast(&QProcess::finished), this, [=](int exitCode, QProcess::ExitStatus exitStatus) { _busyWindow.hide(); }); @@ -511,6 +486,7 @@ void TestRailInterface::addRun() { process->start(_pythonCommand, parameters); } } + void TestRailInterface::updateRunWithResults() { QString filename = _outputDirectory + "/updateRunWithResults.py"; if (QFile::exists(filename)) { @@ -536,17 +512,27 @@ void TestRailInterface::updateRunWithResults() { // The failed tests are read, formatted and inserted into a set // A failure named 'Failure_1--tests.content.entity.material.apply.avatars.00000' is formatted to 'content/entity/material/apply/avatars' // This is the name of the test in TestRail + // + // A success is named `Success_-tests. ... stream << "from os import listdir\n"; stream << "failed_tests = set()\n"; - stream << "for entry in listdir('" + _outputDirectory + "/" + tempName + "'):\n"; - stream << "\tparts = entry.split('--tests.')[1].split('.')\n"; - stream << "\tfailed_test = parts[0]\n"; - stream << "\tfor i in range(1, len(parts) - 1):\n"; - stream << "\t\tfailed_test = failed_test + '/' + parts[i]\n"; + QDir dir(_outputDirectory + "/" + TEMP_NAME); + if (dir.exists()) { + stream << "for entry in listdir('" + _outputDirectory + "/" + TEMP_NAME + "'):\n"; + + // skip over successes + stream << "\tif entry.split('_')[0] == 'Success':\n"; + stream << "\t\tcontinue\n"; + + stream << "\tparts = entry.split('--tests.')[1].split('.')\n"; + stream << "\tfailed_test = parts[0]\n"; + stream << "\tfor i in range(1, len(parts) - 1):\n"; + stream << "\t\tfailed_test = failed_test + '/' + parts[i]\n"; - stream << "\tfailed_tests.add(failed_test)\n\n"; + stream << "\tfailed_tests.add(failed_test)\n\n"; + } // Initialize the array of results that will be eventually used to update TestRail stream << "status_ids = []\n"; @@ -580,7 +566,13 @@ void TestRailInterface::updateRunWithResults() { stream << "\tresults.append({'case_id': case_ids[i], 'status_id': status_ids[i] })\n\n"; stream << "data = { 'results': results }\n"; - stream << "section = client.send_post('add_results_for_cases/' + str(" << runID << "), data)\n"; + stream << "client.send_post('add_results_for_cases/' + str(" << runID << "), data)\n"; + + // Also update the run + QStringList parts = _testResults.split('/'); + QString resultName = parts[parts.length() - 1].split('.')[0]; + stream << "client.send_post('update_run/' + str(" << runID << ")," + << " { 'description' : 'https://hifi-qa.s3.amazonaws.com/" << resultName << "/TestResults.html' })\n"; file.close(); @@ -590,7 +582,7 @@ void TestRailInterface::updateRunWithResults() { ) { QProcess* process = new QProcess(); connect(process, &QProcess::started, this, [=]() { _busyWindow.exec(); }); - + connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); connect(process, static_cast(&QProcess::finished), this, [=](int exitCode, QProcess::ExitStatus exitStatus) { _busyWindow.hide(); }); @@ -765,6 +757,7 @@ void TestRailInterface::getReleasesFromTestRail() { QProcess* process = new QProcess(); connect(process, static_cast(&QProcess::finished), this, [=](int exitCode, QProcess::ExitStatus exitStatus) { updateReleasesComboData(exitCode, exitStatus); }); + connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); QStringList parameters = QStringList() << filename; process->start(_pythonCommand, parameters); @@ -779,10 +772,6 @@ void TestRailInterface::createTestSuitePython(const QString& testDirectory, _userGitHub = userGitHub; _branchGitHub = branchGitHub; - if (!setPythonCommand()) { - return; - } - if (!requestTestRailTestCasesDataFromUser()) { return; } @@ -812,6 +801,7 @@ void TestRailInterface::createTestSuiteXML(const QString& testDirectory, QDomElement suiteName = _document.createElement("name"); suiteName.appendChild( _document.createTextNode("Test Suite - " + QDateTime::currentDateTime().toString("yyyy-MM-ddTHH:mm"))); + topLevelSection.appendChild(suiteName); // This is the first call to 'process'. This is then called recursively to build the full XML tree @@ -908,7 +898,7 @@ QDomElement TestRailInterface::processTestXML(const QString& fullDirectory, ++i; QString title{ words[i] }; for (++i; i < words.length() - 1; ++i) { - title += " / " + words[i]; + title += "/" + words[i]; } QDomElement titleElement = _document.createElement("title"); @@ -1036,7 +1026,6 @@ void TestRailInterface::processTestPython(const QString& fullDirectory, QString testContent = QString("Execute instructions in [THIS TEST](") + testMDName + ")"; QString testExpected = QString("Refer to the expected result in the linked description."); - stream << "data = {\n" << "\t'title': '" << title << "',\n" << "\t'template_id': 2,\n" @@ -1087,6 +1076,7 @@ void TestRailInterface::getTestSectionsFromTestRail() { QProcess* process = new QProcess(); connect(process, static_cast(&QProcess::finished), this, [=](int exitCode, QProcess::ExitStatus exitStatus) { updateSectionsComboData(exitCode, exitStatus); }); + connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); QStringList parameters = QStringList() << filename; process->start(_pythonCommand, parameters); @@ -1125,6 +1115,7 @@ void TestRailInterface::getRunsFromTestRail() { QProcess* process = new QProcess(); connect(process, static_cast(&QProcess::finished), this, [=](int exitCode, QProcess::ExitStatus exitStatus) { updateRunsComboData(exitCode, exitStatus); }); + connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); QStringList parameters = QStringList() << filename; @@ -1134,10 +1125,6 @@ void TestRailInterface::getRunsFromTestRail() { void TestRailInterface::createTestRailRun(const QString& outputDirectory) { _outputDirectory = outputDirectory; - if (!setPythonCommand()) { - return; - } - if (!requestTestRailRunDataFromUser()) { return; } @@ -1151,10 +1138,7 @@ void TestRailInterface::createTestRailRun(const QString& outputDirectory) { void TestRailInterface::updateTestRailRunResults(const QString& testResults, const QString& tempDirectory) { _outputDirectory = tempDirectory; - - if (!setPythonCommand()) { - return; - } + _testResults = testResults; if (!requestTestRailResultsDataFromUser()) { return; @@ -1164,11 +1148,20 @@ void TestRailInterface::updateTestRailRunResults(const QString& testResults, con createTestRailDotPyScript(); // Extract test failures from zipped folder - QString tempSubDirectory = tempDirectory + "/" + tempName; + QString tempSubDirectory = tempDirectory + "/" + TEMP_NAME; QDir dir = tempSubDirectory; dir.mkdir(tempSubDirectory); JlCompress::extractDir(testResults, tempSubDirectory); // TestRail will be updated after the process initiated by getTestRunFromTestRail has completed getRunsFromTestRail(); + + dir.rmdir(tempSubDirectory); +} + +void TestRailInterface::extractTestFailuresFromZippedFolder(const QString& testResults, const QString& tempDirectory) { + QString tempSubDirectory = tempDirectory + "/" + TEMP_NAME; + QDir dir = tempSubDirectory; + dir.mkdir(tempSubDirectory); + JlCompress::extractDir(testResults, tempSubDirectory); } \ No newline at end of file diff --git a/tools/auto-tester/src/TestRailInterface.h b/tools/auto-tester/src/TestRailInterface.h index 6f250dfbba..6843ca0142 100644 --- a/tools/auto-tester/src/TestRailInterface.h +++ b/tools/auto-tester/src/TestRailInterface.h @@ -12,7 +12,6 @@ #define hifi_test_testrail_interface_h #include "ui/BusyWindow.h" - #include "ui/TestRailTestCasesSelectorWindow.h" #include "ui/TestRailRunSelectorWindow.h" #include "ui/TestRailResultsSelectorWindow.h" @@ -22,7 +21,9 @@ #include #include -class TestRailInterface : public QObject{ +#include "PythonInterface.h" + +class TestRailInterface : public QObject { Q_OBJECT public: @@ -65,9 +66,7 @@ public: bool requestTestRailRunDataFromUser(); bool requestTestRailResultsDataFromUser(); - void createAddTestCasesPythonScript(const QString& testDirectory, - const QString& userGitHub, - const QString& branchGitHub); + void createAddTestCasesPythonScript(const QString& testDirectory, const QString& userGitHub, const QString& branchGitHub); void processDirectoryPython(const QString& directory, QTextStream& stream, @@ -88,14 +87,14 @@ public: void addRun(); void updateRunWithResults(); - bool setPythonCommand(); + void extractTestFailuresFromZippedFolder(const QString& testResults, const QString& tempDirectory); private: // HighFidelity Interface project ID in TestRail - const int INTERFACE_PROJECT_ID{ 24 }; + const int INTERFACE_AUTOMATION_PROJECT_ID{ 26 }; // Rendering suite ID - const int INTERFACE_SUITE_ID{ 1147 }; + const int INTERFACE_SUITE_ID{ 1312 }; QDomDocument _document; @@ -111,13 +110,11 @@ private: QString _suiteID; QString _testDirectory; + QString _testResults; QString _outputDirectory; QString _userGitHub; QString _branchGitHub; - const QString pythonExe{ "python.exe" }; - QString _pythonCommand; - QStringList _releaseNames; QStringList _sectionNames; @@ -126,7 +123,10 @@ private: QStringList _runNames; std::vector _runIDs; - QString tempName{ "fgadhcUDHSFaidsfh3478JJJFSDFIUSOEIrf" }; + QString TEMP_NAME{ "fgadhcUDHSFaidsfh3478JJJFSDFIUSOEIrf" }; + + PythonInterface* _pythonInterface; + QString _pythonCommand; }; #endif \ No newline at end of file diff --git a/tools/auto-tester/src/TestRunner.cpp b/tools/auto-tester/src/TestRunner.cpp new file mode 100644 index 0000000000..674cf6f8e8 --- /dev/null +++ b/tools/auto-tester/src/TestRunner.cpp @@ -0,0 +1,608 @@ +// +// TestRunner.cpp +// +// Created by Nissim Hadar on 1 Sept 2018. +// Copyright 2013 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 "TestRunner.h" + +#include +#include +#include + +#include "ui/AutoTester.h" +extern AutoTester* autoTester; + +#ifdef Q_OS_WIN +#include +#include +#endif + +TestRunner::TestRunner(std::vector dayCheckboxes, + std::vector timeEditCheckboxes, + std::vector timeEdits, + QLabel* workingFolderLabel, + QCheckBox* runServerless, + QCheckBox* runLatest, + QLineEdit* url, + QPushButton* runNow, + QObject* parent) : + QObject(parent) { + _dayCheckboxes = dayCheckboxes; + _timeEditCheckboxes = timeEditCheckboxes; + _timeEdits = timeEdits; + _workingFolderLabel = workingFolderLabel; + _runServerless = runServerless; + _runLatest = runLatest; + _url = url; + _runNow = runNow; + + installerThread = new QThread(); + installerWorker = new Worker(); + installerWorker->moveToThread(installerThread); + installerThread->start(); + connect(this, SIGNAL(startInstaller()), installerWorker, SLOT(runCommand())); + connect(installerWorker, SIGNAL(commandComplete()), this, SLOT(installationComplete())); + + interfaceThread = new QThread(); + interfaceWorker = new Worker(); + interfaceThread->start(); + interfaceWorker->moveToThread(interfaceThread); + connect(this, SIGNAL(startInterface()), interfaceWorker, SLOT(runCommand())); + connect(interfaceWorker, SIGNAL(commandComplete()), this, SLOT(interfaceExecutionComplete())); +} + +TestRunner::~TestRunner() { + delete installerThread; + delete interfaceThread; + + delete interfaceThread; + delete interfaceWorker; + + if (_timer) { + delete _timer; + } +} + +void TestRunner::setWorkingFolder() { + // Everything will be written to this folder + QString previousSelection = _workingFolder; + QString parent = previousSelection.left(previousSelection.lastIndexOf('/')); + if (!parent.isNull() && parent.right(1) != "/") { + parent += "/"; + } + + _workingFolder = QFileDialog::getExistingDirectory(nullptr, "Please select a temporary folder for installation", parent, + QFileDialog::ShowDirsOnly); + + // If user canceled then restore previous selection and return + if (_workingFolder == "") { + _workingFolder = previousSelection; + return; + } + + _installationFolder = _workingFolder + "/High Fidelity"; + _logFile.setFileName(_workingFolder + "/log.txt"); + + autoTester->enableRunTabControls(); + _workingFolderLabel->setText(QDir::toNativeSeparators(_workingFolder)); + + _timer = new QTimer(this); + connect(_timer, SIGNAL(timeout()), this, SLOT(checkTime())); + _timer->start(30 * 1000); //time specified in ms +} + +void TestRunner::run() { + _runNow->setEnabled(false); + + _testStartDateTime = QDateTime::currentDateTime(); + _automatedTestIsRunning = true; + + // Initial setup + _branch = autoTester->getSelectedBranch(); + _user = autoTester->getSelectedUser(); + + // This will be restored at the end of the tests + saveExistingHighFidelityAppDataFolder(); + + // Download the latest High Fidelity build XML. + // Note that this is not needed for PR builds (or whenever `Run Latest` is unchecked) + // It is still downloaded, to simplify the flow + QStringList urls; + QStringList filenames; + + urls << DEV_BUILD_XML_URL; + filenames << DEV_BUILD_XML_FILENAME; + + updateStatusLabel("Downloading Build XML"); + + buildXMLDownloaded = false; + autoTester->downloadFiles(urls, _workingFolder, filenames, (void*)this); + + // `downloadComplete` will run after download has completed +} + +void TestRunner::downloadComplete() { + if (!buildXMLDownloaded) { + // Download of Build XML has completed + buildXMLDownloaded = true; + + // Download the High Fidelity installer + QStringList urls; + QStringList filenames; + if (_runLatest->isChecked()) { + parseBuildInformation(); + + _installerFilename = INSTALLER_FILENAME_LATEST; + + urls << _buildInformation.url; + filenames << _installerFilename; + } else { + QString urlText = _url->text(); + urls << urlText; + _installerFilename = getInstallerNameFromURL(urlText); + filenames << _installerFilename; + } + + updateStatusLabel("Downloading installer"); + + autoTester->downloadFiles(urls, _workingFolder, filenames, (void*)this); + + // `downloadComplete` will run again after download has completed + + } else { + // Download of Installer has completed + appendLog(QString("Tests started at ") + QString::number(_testStartDateTime.time().hour()) + ":" + + QString("%1").arg(_testStartDateTime.time().minute(), 2, 10, QChar('0')) + ", on " + + _testStartDateTime.date().toString("ddd, MMM d, yyyy")); + + updateStatusLabel("Installing"); + + // Kill any existing processes that would interfere with installation + killProcesses(); + + runInstaller(); + } +} + +void TestRunner::runInstaller() { + // Qt cannot start an installation process using QProcess::start (Qt Bug 9761) + // To allow installation, the installer is run using the `system` command + + QStringList arguments{ QStringList() << QString("/S") << QString("/D=") + QDir::toNativeSeparators(_installationFolder) }; + + QString installerFullPath = _workingFolder + "/" + _installerFilename; + + QString commandLine = + "\"" + QDir::toNativeSeparators(installerFullPath) + "\"" + " /S /D=" + QDir::toNativeSeparators(_installationFolder); + + installerWorker->setCommandLine(commandLine); + emit startInstaller(); +} + +void TestRunner::installationComplete() { + verifyInstallationSucceeded(); + + createSnapshotFolder(); + + updateStatusLabel("Running tests"); + + if (!_runServerless->isChecked()) { + startLocalServerProcesses(); + } + + runInterfaceWithTestScript(); +} + +void TestRunner::verifyInstallationSucceeded() { + // Exit if the executables are missing. + // On Windows, the reason is probably that UAC has blocked the installation. This is treated as a critical error +#ifdef Q_OS_WIN + QFileInfo interfaceExe(QDir::toNativeSeparators(_installationFolder) + "\\interface.exe"); + QFileInfo assignmentClientExe(QDir::toNativeSeparators(_installationFolder) + "\\assignment-client.exe"); + QFileInfo domainServerExe(QDir::toNativeSeparators(_installationFolder) + "\\domain-server.exe"); + + if (!interfaceExe.exists() || !assignmentClientExe.exists() || !domainServerExe.exists()) { + QMessageBox::critical(0, "Installation of High Fidelity has failed", "Please verify that UAC has been disabled"); + exit(-1); + } +#endif +} + +void TestRunner::saveExistingHighFidelityAppDataFolder() { + QString dataDirectory{ "NOT FOUND" }; + +#ifdef Q_OS_WIN + dataDirectory = qgetenv("USERPROFILE") + "\\AppData\\Roaming"; +#endif + + if (_runLatest->isChecked()) { + _appDataFolder = dataDirectory + "\\High Fidelity"; + } else { + // We are running a PR build + _appDataFolder = dataDirectory + "\\High Fidelity - " + getPRNumberFromURL(_url->text()); + } + + _savedAppDataFolder = dataDirectory + "/" + UNIQUE_FOLDER_NAME; + if (_savedAppDataFolder.exists()) { + _savedAppDataFolder.removeRecursively(); + } + + if (_appDataFolder.exists()) { + // The original folder is saved in a unique name + _appDataFolder.rename(_appDataFolder.path(), _savedAppDataFolder.path()); + } + + // Copy an "empty" AppData folder (i.e. no entities) + copyFolder(QDir::currentPath() + "/AppDataHighFidelity", _appDataFolder.path()); +} + +void TestRunner::createSnapshotFolder() { + _snapshotFolder = _workingFolder + "/" + SNAPSHOT_FOLDER_NAME; + + // Just delete all PNGs from the folder if it already exists + if (QDir(_snapshotFolder).exists()) { + // Note that we cannot use just a `png` filter, as the filenames include periods + // Also, delete any `jpg` and `txt` files + // The idea is to leave only previous zipped result folders + QDirIterator it(_snapshotFolder.toStdString().c_str()); + while (it.hasNext()) { + QString filename = it.next(); + if (filename.right(4) == ".png" || filename.right(4) == ".jpg" || filename.right(4) == ".txt") { + QFile::remove(filename); + } + } + + } else { + QDir().mkdir(_snapshotFolder); + } +} + +void TestRunner::killProcesses() { +#ifdef Q_OS_WIN + try { + QStringList processesToKill = QStringList() << "interface.exe" + << "assignment-client.exe" + << "domain-server.exe" + << "server-console.exe"; + + // Loop until all pending processes to kill have actually died + QStringList pendingProcessesToKill; + do { + pendingProcessesToKill.clear(); + + // Get list of running tasks + HANDLE processSnapHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (processSnapHandle == INVALID_HANDLE_VALUE) { + throw("Process snapshot creation failure"); + } + + PROCESSENTRY32 processEntry32; + processEntry32.dwSize = sizeof(PROCESSENTRY32); + if (!Process32First(processSnapHandle, &processEntry32)) { + CloseHandle(processSnapHandle); + throw("Process32First failed"); + } + + // Kill any task in the list + do { + foreach (QString process, processesToKill) + if (QString(processEntry32.szExeFile) == process) { + QString commandLine = "taskkill /im " + process + " /f >nul"; + system(commandLine.toStdString().c_str()); + pendingProcessesToKill << process; + } + } while (Process32Next(processSnapHandle, &processEntry32)); + + QThread::sleep(2); + } while (!pendingProcessesToKill.isEmpty()); + + } catch (QString errorMessage) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), errorMessage); + exit(-1); + } catch (...) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "unknown error"); + exit(-1); + } +#endif +} + +void TestRunner::startLocalServerProcesses() { +#ifdef Q_OS_WIN + QString commandLine; + + commandLine = "start \"domain-server.exe\" \"" + QDir::toNativeSeparators(_installationFolder) + "\\domain-server.exe\""; + system(commandLine.toStdString().c_str()); + + commandLine = + "start \"assignment-client.exe\" \"" + QDir::toNativeSeparators(_installationFolder) + "\\assignment-client.exe\" -n 6"; + system(commandLine.toStdString().c_str()); +#endif + // Give server processes time to stabilize + QThread::sleep(20); +} + +void TestRunner::runInterfaceWithTestScript() { + QString exeFile = QString("\"") + QDir::toNativeSeparators(_installationFolder) + "\\interface.exe\""; + QString snapshotFolder = QString("\"") + QDir::toNativeSeparators(_snapshotFolder) + "\""; + + QString url = QString("hifi://localhost"); + if (_runServerless->isChecked()) { + // Move to an empty area + url = "file:///~serverless/tutorial.json"; + } else { + url = "hifi://localhost"; + } + + QString testScript = + QString("https://raw.githubusercontent.com/") + _user + "/hifi_tests/" + _branch + "/tests/testRecursive.js"; + + QString commandLine = exeFile + " --url " + url + " --no-updater --no-login" + " --testScript " + testScript + + " quitWhenFinished --testResultsLocation " + snapshotFolder; + + interfaceWorker->setCommandLine(commandLine); + emit startInterface(); +} + +void TestRunner::interfaceExecutionComplete() { + killProcesses(); + + QFileInfo testCompleted(QDir::toNativeSeparators(_snapshotFolder) +"/tests_completed.txt"); + if (!testCompleted.exists()) { + QMessageBox::critical(0, "Tests not completed", "Interface seems to have crashed before completion of the test scripts"); + _runNow->setEnabled(true); + return; + } + + evaluateResults(); + + // The High Fidelity AppData folder will be restored after evaluation has completed +} + +void TestRunner::evaluateResults() { + updateStatusLabel("Evaluating results"); + autoTester->startTestsEvaluation(false, true, _snapshotFolder, _branch, _user); +} + +void TestRunner::automaticTestRunEvaluationComplete(QString zippedFolder, int numberOfFailures) { + addBuildNumberToResults(zippedFolder); + restoreHighFidelityAppDataFolder(); + + updateStatusLabel("Testing complete"); + + QDateTime currentDateTime = QDateTime::currentDateTime(); + + QString completionText = QString("Tests completed at ") + QString::number(currentDateTime.time().hour()) + ":" + + QString("%1").arg(currentDateTime.time().minute(), 2, 10, QChar('0')) + ", on " + + currentDateTime.date().toString("ddd, MMM d, yyyy"); + + if (numberOfFailures == 0) { + completionText += "; no failures"; + } else if (numberOfFailures == 1) { + completionText += "; 1 failure"; + } else { + completionText += QString("; ") + QString::number(numberOfFailures) + " failures"; + } + appendLog(completionText); + + _automatedTestIsRunning = false; + + _runNow->setEnabled(true); +} + +void TestRunner::addBuildNumberToResults(QString zippedFolderName) { + QString augmentedFilename; + if (!_runLatest->isChecked()) { + augmentedFilename = zippedFolderName.replace("local", getPRNumberFromURL(_url->text())); + } else { + augmentedFilename = zippedFolderName.replace("local", _buildInformation.build); + } + QFile::rename(zippedFolderName, augmentedFilename); +} + +void TestRunner::restoreHighFidelityAppDataFolder() { + _appDataFolder.removeRecursively(); + + if (_savedAppDataFolder != QDir()) { + _appDataFolder.rename(_savedAppDataFolder.path(), _appDataFolder.path()); + } +} + +// Copies a folder recursively +void TestRunner::copyFolder(const QString& source, const QString& destination) { + try { + if (!QFileInfo(source).isDir()) { + // just a file copy + QFile::copy(source, destination); + } else { + QDir destinationDir(destination); + if (!destinationDir.cdUp()) { + throw("'source '" + source + "'seems to be a root folder"); + } + + if (!destinationDir.mkdir(QFileInfo(destination).fileName())) { + throw("Could not create destination folder '" + destination + "'"); + } + + QStringList fileNames = + QDir(source).entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System); + + foreach (const QString& fileName, fileNames) { + copyFolder(QString(source + "/" + fileName), QString(destination + "/" + fileName)); + } + } + } catch (QString errorMessage) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), errorMessage); + exit(-1); + } catch (...) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "unknown error"); + exit(-1); + } +} + +void TestRunner::checkTime() { + // No processing is done if a test is running + if (_automatedTestIsRunning) { + return; + } + + QDateTime now = QDateTime::currentDateTime(); + + // Check day of week + if (!_dayCheckboxes.at(now.date().dayOfWeek() - 1)->isChecked()) { + return; + } + + // Check the time + bool timeToRun{ false }; + + for (size_t i = 0; i < std::min(_timeEditCheckboxes.size(), _timeEdits.size()); ++i) { + if (_timeEditCheckboxes[i]->isChecked() && (_timeEdits[i]->time().hour() == now.time().hour()) && + (_timeEdits[i]->time().minute() == now.time().minute())) { + timeToRun = true; + break; + } + } + + if (timeToRun) { + run(); + } +} + +void TestRunner::updateStatusLabel(const QString& message) { + autoTester->updateStatusLabel(message); +} + +void TestRunner::appendLog(const QString& message) { + if (!_logFile.open(QIODevice::Append | QIODevice::Text)) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), + "Could not open the log file"); + exit(-1); + } + + _logFile.write(message.toStdString().c_str()); + _logFile.write("\n"); + _logFile.close(); + + autoTester->appendLogWindow(message); +} + +QString TestRunner::getInstallerNameFromURL(const QString& url) { + // An example URL: https://deployment.highfidelity.com/jobs/pr-build/label%3Dwindows/13023/HighFidelity-Beta-Interface-PR14006-be76c43.exe + try { + QStringList urlParts = url.split("/"); + return urlParts[urlParts.size() - 1]; + } catch (QString errorMessage) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), errorMessage); + exit(-1); + } catch (...) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "unknown error"); + exit(-1); + } +} + +QString TestRunner::getPRNumberFromURL(const QString& url) { + try { + QStringList urlParts = url.split("/"); + QStringList filenameParts = urlParts[urlParts.size() - 1].split("-"); + if (filenameParts.size() <= 3) { + throw "URL not in expected format, should look like `https://deployment.highfidelity.com/jobs/pr-build/label%3Dwindows/13023/HighFidelity-Beta-Interface-PR14006-be76c43.exe`"; + } + return filenameParts[filenameParts.size() - 2]; + } catch (QString errorMessage) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), errorMessage); + exit(-1); + } catch (...) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "unknown error"); + exit(-1); + } +} + +void TestRunner::parseBuildInformation() { + try { + QDomDocument domDocument; + QString filename{ _workingFolder + "/" + DEV_BUILD_XML_FILENAME }; + QFile file(filename); + if (!file.open(QIODevice::ReadOnly) || !domDocument.setContent(&file)) { + throw QString("Could not open " + filename); + } + + QString platformOfInterest; +#ifdef Q_OS_WIN + platformOfInterest = "windows"; +#elif defined(Q_OS_MAC) + platformOfInterest = "mac"; +#endif + QDomElement element = domDocument.documentElement(); + + // Verify first element is "projects" + if (element.tagName() != "projects") { + throw("File seems to be in wrong format"); + } + + element = element.firstChild().toElement(); + if (element.tagName() != "project") { + throw("File seems to be in wrong format"); + } + + if (element.attribute("name") != "interface") { + throw("File is not from 'interface' build"); + } + + // Now loop over the platforms + while (!element.isNull()) { + element = element.firstChild().toElement(); + if (element.tagName() != "platform" || element.attribute("name") != platformOfInterest) { + continue; + } + + // Next element should be the build + element = element.firstChild().toElement(); + if (element.tagName() != "build") { + throw("File seems to be in wrong format"); + } + + // Next element should be the version + element = element.firstChild().toElement(); + if (element.tagName() != "version") { + throw("File seems to be in wrong format"); + } + + // Add the build number to the end of the filename + _buildInformation.build = element.text(); + + // First sibling should be stable_version + element = element.nextSibling().toElement(); + if (element.tagName() != "stable_version") { + throw("File seems to be in wrong format"); + } + + // Next sibling should be url + element = element.nextSibling().toElement(); + if (element.tagName() != "url") { + throw("File seems to be in wrong format"); + } + _buildInformation.url = element.text(); + } + + } catch (QString errorMessage) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), errorMessage); + exit(-1); + } catch (...) { + QMessageBox::critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "unknown error"); + exit(-1); + } +} + +void Worker::setCommandLine(const QString& commandLine) { + _commandLine = commandLine; +} + +int Worker::runCommand() { + int result = system(_commandLine.toStdString().c_str()); + emit commandComplete(); + return result; +} \ No newline at end of file diff --git a/tools/auto-tester/src/TestRunner.h b/tools/auto-tester/src/TestRunner.h new file mode 100644 index 0000000000..e6cb7cd764 --- /dev/null +++ b/tools/auto-tester/src/TestRunner.h @@ -0,0 +1,151 @@ +// +// TestRunner.h +// +// Created by Nissim Hadar on 1 Sept 2018. +// Copyright 2013 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_testRunner_h +#define hifi_testRunner_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class BuildInformation { +public: + QString build; + QString url; +}; + +class Worker; + +class TestRunner : public QObject { + Q_OBJECT +public: + explicit TestRunner(std::vector dayCheckboxes, + std::vector timeEditCheckboxes, + std::vector timeEdits, + QLabel* workingFolderLabel, + QCheckBox* runServerless, + QCheckBox* runLatest, + QLineEdit* url, + QPushButton* runNow, + QObject* parent = 0); + + ~TestRunner(); + + void setWorkingFolder(); + + void run(); + + void downloadComplete(); + void runInstaller(); + void verifyInstallationSucceeded(); + + void saveExistingHighFidelityAppDataFolder(); + void restoreHighFidelityAppDataFolder(); + + void createSnapshotFolder(); + + void killProcesses(); + void startLocalServerProcesses(); + + void runInterfaceWithTestScript(); + + void evaluateResults(); + void automaticTestRunEvaluationComplete(QString zippedFolderName, int numberOfFailures); + void addBuildNumberToResults(QString zippedFolderName); + + void copyFolder(const QString& source, const QString& destination); + + void updateStatusLabel(const QString& message); + void appendLog(const QString& message); + + QString getInstallerNameFromURL(const QString& url); + QString getPRNumberFromURL(const QString& url); + + void parseBuildInformation(); + +private slots: + void checkTime(); + void installationComplete(); + void interfaceExecutionComplete(); + +signals: + void startInstaller(); + void startInterface(); + +private: + bool _automatedTestIsRunning{ false }; + + const QString INSTALLER_FILENAME_LATEST{ "HighFidelity-Beta-latest-dev.exe" }; + + QString _installerURL; + QString _installerFilename; + const QString DEV_BUILD_XML_URL{ "https://highfidelity.com/dev-builds.xml" }; + const QString DEV_BUILD_XML_FILENAME{ "dev-builds.xml" }; + + bool buildXMLDownloaded; + + QDir _appDataFolder; + QDir _savedAppDataFolder; + + QString _workingFolder; + QString _installationFolder; + QString _snapshotFolder; + + const QString UNIQUE_FOLDER_NAME{ "fgadhcUDHSFaidsfh3478JJJFSDFIUSOEIrf" }; + const QString SNAPSHOT_FOLDER_NAME{ "snapshots" }; + + QString _branch; + QString _user; + + std::vector _dayCheckboxes; + std::vector _timeEditCheckboxes; + std::vector _timeEdits; + QLabel* _workingFolderLabel; + QCheckBox* _runServerless; + QCheckBox* _runLatest; + QLineEdit* _url; + QPushButton* _runNow; + QTimer* _timer; + + QFile _logFile; + + QDateTime _testStartDateTime; + + QThread* installerThread; + QThread* interfaceThread; + Worker* installerWorker; + Worker* interfaceWorker; + + BuildInformation _buildInformation; +}; + +class Worker : public QObject { + Q_OBJECT +public: + void setCommandLine(const QString& commandLine); + +public slots: + int runCommand(); + +signals: + void commandComplete(); + void startInstaller(); + void startInterface(); + +private: + QString _commandLine; +}; +#endif // hifi_testRunner_h \ No newline at end of file diff --git a/tools/auto-tester/src/common.h b/tools/auto-tester/src/common.h index 939814df62..5df4e9c921 100644 --- a/tools/auto-tester/src/common.h +++ b/tools/auto-tester/src/common.h @@ -12,9 +12,9 @@ #include -class TestFailure { +class TestResult { public: - TestFailure(float error, QString pathname, QString expectedImageFilename, QString actualImageFilename) : + TestResult(float error, QString pathname, QString expectedImageFilename, QString actualImageFilename) : _error(error), _pathname(pathname), _expectedImageFilename(expectedImageFilename), diff --git a/tools/auto-tester/src/main.cpp b/tools/auto-tester/src/main.cpp index 03b8cf51ff..ac4b4593c5 100644 --- a/tools/auto-tester/src/main.cpp +++ b/tools/auto-tester/src/main.cpp @@ -66,7 +66,7 @@ int main(int argc, char *argv[]) { autoTester->setup(); if (!testFolder.isNull()) { - autoTester->runFromCommandLine(testFolder, branch, user); + autoTester->startTestsEvaluation(true ,false, testFolder, branch, user); } else { autoTester->show(); } diff --git a/tools/auto-tester/src/ui/AutoTester.cpp b/tools/auto-tester/src/ui/AutoTester.cpp index 13bda4853f..32457c2224 100644 --- a/tools/auto-tester/src/ui/AutoTester.cpp +++ b/tools/auto-tester/src/ui/AutoTester.cpp @@ -28,34 +28,86 @@ AutoTester::AutoTester(QWidget* parent) : QMainWindow(parent) { connect(_ui.actionAbout, &QAction::triggered, this, &AutoTester::about); connect(_ui.actionContent, &QAction::triggered, this, &AutoTester::content); + // The second tab hides and shows the Windows task bar #ifndef Q_OS_WIN - _ui.tabWidget->setTabEnabled(3, false); + _ui.tabWidget->removeTab(1); #endif - // helpWindow.textBrowser->setText() + _ui.statusLabel->setText(""); + _ui.plainTextEdit->setReadOnly(true); + + setWindowTitle("Auto Tester - v6.7"); + + // Coming soon to an auto-tester near you... + //// _helpWindow.textBrowser->setText() +} + +AutoTester::~AutoTester() { + delete _signalMapper; + + if (_test) { + delete _test; + } + + if (_testRunner) { + delete _testRunner; + } } void AutoTester::setup() { - _test = new Test(); + if (_test) { + delete _test; + } + _test = new Test(_ui.progressBar, _ui.checkBoxInteractiveMode); + + std::vector dayCheckboxes; + dayCheckboxes.emplace_back(_ui.mondayCheckBox); + dayCheckboxes.emplace_back(_ui.tuesdayCheckBox); + dayCheckboxes.emplace_back(_ui.wednesdayCheckBox); + dayCheckboxes.emplace_back(_ui.thursdayCheckBox); + dayCheckboxes.emplace_back(_ui.fridayCheckBox); + dayCheckboxes.emplace_back(_ui.saturdayCheckBox); + dayCheckboxes.emplace_back(_ui.sundayCheckBox); + + std::vector timeEditCheckboxes; + timeEditCheckboxes.emplace_back(_ui.timeEdit1checkBox); + timeEditCheckboxes.emplace_back(_ui.timeEdit2checkBox); + timeEditCheckboxes.emplace_back(_ui.timeEdit3checkBox); + timeEditCheckboxes.emplace_back(_ui.timeEdit4checkBox); + + std::vector timeEdits; + timeEdits.emplace_back(_ui.timeEdit1); + timeEdits.emplace_back(_ui.timeEdit2); + timeEdits.emplace_back(_ui.timeEdit3); + timeEdits.emplace_back(_ui.timeEdit4); + + if (_testRunner) { + delete _testRunner; + } + _testRunner = new TestRunner(dayCheckboxes, timeEditCheckboxes, timeEdits, _ui.workingFolderLabel, _ui.checkBoxServerless, _ui.checkBoxRunLatest, _ui.urlLineEdit, _ui.runNowButton); } -void AutoTester::runFromCommandLine(const QString& testFolder, const QString& branch, const QString& user) { - _isRunningFromCommandline = true; - _test->startTestsEvaluation(testFolder, branch, user); +void AutoTester::startTestsEvaluation(const bool isRunningFromCommandLine, + const bool isRunningInAutomaticTestRun, + const QString& snapshotDirectory, + const QString& branch, + const QString& user +) { + _test->startTestsEvaluation(isRunningFromCommandLine, isRunningInAutomaticTestRun, snapshotDirectory, branch, user); } void AutoTester::on_tabWidget_currentChanged(int index) { - if (index == 1 || index == 2) { - _ui.userTextEdit->setDisabled(false); - _ui.branchTextEdit->setDisabled(false); + if (index == 0 || index == 2 || index == 3) { + _ui.userLineEdit->setDisabled(false); + _ui.branchLineEdit->setDisabled(false); } else { - _ui.userTextEdit->setDisabled(true); - _ui.branchTextEdit->setDisabled(true); + _ui.userLineEdit->setDisabled(true); + _ui.branchLineEdit->setDisabled(true); } } void AutoTester::on_evaluateTestsButton_clicked() { - _test->startTestsEvaluation(); + _test->startTestsEvaluation(false, false); } void AutoTester::on_createRecursiveScriptButton_clicked() { @@ -78,6 +130,14 @@ void AutoTester::on_createAllMDFilesButton_clicked() { _test->createAllMDFiles(); } +void AutoTester::on_createTestAutoScriptButton_clicked() { + _test->createTestAutoScript(); +} + +void AutoTester::on_createAllTestAutoScriptsButton_clicked() { + _test->createAllTestAutoScripts(); +} + void AutoTester::on_createTestsOutlineButton_clicked() { _test->createTestsOutline(); } @@ -90,6 +150,28 @@ void AutoTester::on_createTestRailRunButton_clicked() { _test->createTestRailRun(); } +void AutoTester::on_setWorkingFolderButton_clicked() { + _testRunner->setWorkingFolder(); +} + +void AutoTester::enableRunTabControls() { + _ui.runNowButton->setEnabled(true); + _ui.daysGroupBox->setEnabled(true); + _ui.timesGroupBox->setEnabled(true); +} + +void AutoTester::on_runNowButton_clicked() { + _testRunner->run(); +} + +void AutoTester::on_checkBoxRunLatest_clicked() { + _ui.urlLineEdit->setEnabled(!_ui.checkBoxRunLatest->isChecked()); +} + +void AutoTester::automaticTestRunEvaluationComplete(QString zippedFolderName, int numberOfFailures) { + _testRunner->automaticTestRunEvaluationComplete(zippedFolderName, numberOfFailures); +} + void AutoTester::on_updateTestRailRunResultsButton_clicked() { _test->updateTestRailRunResult(); } @@ -130,7 +212,11 @@ void AutoTester::on_createXMLScriptRadioButton_clicked() { _test->setTestRailCreateMode(XML); } -void AutoTester::downloadImage(const QUrl& url) { +void AutoTester::on_createWebPagePushButton_clicked() { + _test->createWebPage(_ui.updateAWSCheckBox, _ui.awsURLLineEdit); +} + +void AutoTester::downloadFile(const QUrl& url) { _downloaders.emplace_back(new Downloader(url, this)); connect(_downloaders[_index], SIGNAL(downloaded()), _signalMapper, SLOT(map())); @@ -139,70 +225,86 @@ void AutoTester::downloadImage(const QUrl& url) { ++_index; } -void AutoTester::downloadImages(const QStringList& URLs, const QString& directoryName, const QStringList& filenames) { +void AutoTester::downloadFiles(const QStringList& URLs, const QString& directoryName, const QStringList& filenames, void *caller) { + connect(_signalMapper, SIGNAL(mapped(int)), this, SLOT(saveFile(int))); + _directoryName = directoryName; _filenames = filenames; + _caller = caller; - _numberOfImagesToDownload = URLs.size(); - _numberOfImagesDownloaded = 0; + _numberOfFilesToDownload = URLs.size(); + _numberOfFilesDownloaded = 0; _index = 0; _ui.progressBar->setMinimum(0); - _ui.progressBar->setMaximum(_numberOfImagesToDownload - 1); + _ui.progressBar->setMaximum(_numberOfFilesToDownload - 1); _ui.progressBar->setValue(0); _ui.progressBar->setVisible(true); - _downloaders.clear(); - for (int i = 0; i < _numberOfImagesToDownload; ++i) { - QUrl imageURL(URLs[i]); - downloadImage(imageURL); + foreach (auto downloader, _downloaders) { + delete downloader; } - connect(_signalMapper, SIGNAL(mapped(int)), this, SLOT(saveImage(int))); + _downloaders.clear(); + for (int i = 0; i < _numberOfFilesToDownload; ++i) { + downloadFile(URLs[i]); + } } -void AutoTester::saveImage(int index) { +void AutoTester::saveFile(int index) { try { QFile file(_directoryName + "/" + _filenames[index]); file.open(QIODevice::WriteOnly); file.write(_downloaders[index]->downloadedData()); file.close(); } catch (...) { - QMessageBox::information(0, "Test Aborted", "Failed to save image: " + _filenames[index]); + QMessageBox::information(0, "Test Aborted", "Failed to save file: " + _filenames[index]); _ui.progressBar->setVisible(false); return; } - ++_numberOfImagesDownloaded; + ++_numberOfFilesDownloaded; - if (_numberOfImagesDownloaded == _numberOfImagesToDownload) { - disconnect(_signalMapper, SIGNAL(mapped(int)), this, SLOT(saveImage(int))); - _test->finishTestsEvaluation(_isRunningFromCommandline, _ui.checkBoxInteractiveMode->isChecked(), _ui.progressBar); + if (_numberOfFilesDownloaded == _numberOfFilesToDownload) { + disconnect(_signalMapper, SIGNAL(mapped(int)), this, SLOT(saveFile(int))); + if (_caller == _test) { + _test->finishTestsEvaluation(); + } else if (_caller == _testRunner) { + _testRunner->downloadComplete(); + } } else { - _ui.progressBar->setValue(_numberOfImagesDownloaded); + _ui.progressBar->setValue(_numberOfFilesDownloaded); } } void AutoTester::about() { - QMessageBox::information(0, "About", QString("Built ") + __DATE__ + " : " + __TIME__); + QMessageBox::information(0, "About", QString("Built ") + __DATE__ + ", " + __TIME__); } void AutoTester::content() { - helpWindow.show(); + _helpWindow.show(); } void AutoTester::setUserText(const QString& user) { - _ui.userTextEdit->setText(user); + _ui.userLineEdit->setText(user); } QString AutoTester::getSelectedUser() { - return _ui.userTextEdit->toPlainText(); + return _ui.userLineEdit->text(); } void AutoTester::setBranchText(const QString& branch) { - _ui.branchTextEdit->setText(branch); + _ui.branchLineEdit->setText(branch); } QString AutoTester::getSelectedBranch() { - return _ui.branchTextEdit->toPlainText(); + return _ui.branchLineEdit->text(); } + +void AutoTester::updateStatusLabel(const QString& status) { + _ui.statusLabel->setText(status); +} + +void AutoTester::appendLogWindow(const QString& message) { + _ui.plainTextEdit->appendPlainText(message); +} \ No newline at end of file diff --git a/tools/auto-tester/src/ui/AutoTester.h b/tools/auto-tester/src/ui/AutoTester.h index e29da5b716..429a8b60e1 100644 --- a/tools/auto-tester/src/ui/AutoTester.h +++ b/tools/auto-tester/src/ui/AutoTester.h @@ -19,19 +19,28 @@ #include "../Test.h" #include "HelpWindow.h" +#include "../TestRunner.h" +#include "../AWSInterface.h" class AutoTester : public QMainWindow { Q_OBJECT public: - AutoTester(QWidget *parent = Q_NULLPTR); + AutoTester(QWidget* parent = Q_NULLPTR); + ~AutoTester(); void setup(); - void runFromCommandLine(const QString& testFolder, const QString& branch, const QString& user); + void startTestsEvaluation(const bool isRunningFromCommandLine, + const bool isRunningInAutomaticTestRun, + const QString& snapshotDirectory, + const QString& branch, + const QString& user); - void downloadImage(const QUrl& url); - void downloadImages(const QStringList& URLs, const QString& directoryName, const QStringList& filenames); + void automaticTestRunEvaluationComplete(QString zippedFolderName, int numberOfFailures); + + void downloadFile(const QUrl& url); + void downloadFiles(const QStringList& URLs, const QString& directoryName, const QStringList& filenames, void* caller); void setUserText(const QString& user); QString getSelectedUser(); @@ -39,21 +48,35 @@ public: void setBranchText(const QString& branch); QString getSelectedBranch(); + void enableRunTabControls(); + + void updateStatusLabel(const QString& status); + void appendLogWindow(const QString& message); + private slots: void on_tabWidget_currentChanged(int index); void on_evaluateTestsButton_clicked(); void on_createRecursiveScriptButton_clicked(); void on_createAllRecursiveScriptsButton_clicked(); - void on_createTestsButton_clicked(); + void on_createTestsButton_clicked(); void on_createMDFileButton_clicked(); void on_createAllMDFilesButton_clicked(); + void on_createTestAutoScriptButton_clicked(); + void on_createAllTestAutoScriptsButton_clicked(); + void on_createTestsOutlineButton_clicked(); void on_createTestRailTestCasesButton_clicked(); void on_createTestRailRunButton_clicked(); + + void on_setWorkingFolderButton_clicked(); + void on_runNowButton_clicked(); + + void on_checkBoxRunLatest_clicked(); + void on_updateTestRailRunResultsButton_clicked(); void on_hideTaskbarButton_clicked(); @@ -62,16 +85,21 @@ private slots: void on_createPythonScriptRadioButton_clicked(); void on_createXMLScriptRadioButton_clicked(); + void on_createWebPagePushButton_clicked(); + void on_closeButton_clicked(); - void saveImage(int index); + void saveFile(int index); void about(); void content(); private: Ui::AutoTesterClass _ui; - Test* _test; + Test* _test{ nullptr }; + TestRunner* _testRunner{ nullptr }; + + AWSInterface _awsInterface; std::vector _downloaders; @@ -82,13 +110,15 @@ private: // Used to enable passing a parameter to slots QSignalMapper* _signalMapper; - int _numberOfImagesToDownload { 0 }; - int _numberOfImagesDownloaded { 0 }; - int _index { 0 }; + int _numberOfFilesToDownload{ 0 }; + int _numberOfFilesDownloaded{ 0 }; + int _index{ 0 }; - bool _isRunningFromCommandline { false }; + bool _isRunningFromCommandline{ false }; - HelpWindow helpWindow; + HelpWindow _helpWindow; + + void* _caller; }; -#endif // hifi_AutoTester_h \ No newline at end of file +#endif // hifi_AutoTester_h \ No newline at end of file diff --git a/tools/auto-tester/src/ui/AutoTester.ui b/tools/auto-tester/src/ui/AutoTester.ui index ac8fcf5e86..b277fbdb2a 100644 --- a/tools/auto-tester/src/ui/AutoTester.ui +++ b/tools/auto-tester/src/ui/AutoTester.ui @@ -6,8 +6,8 @@ 0 0 - 432 - 734 + 720 + 870 @@ -23,8 +23,8 @@ - 166 - 610 + 470 + 750 100 40 @@ -36,10 +36,10 @@ - 12 + 45 140 - 408 - 461 + 630 + 580 @@ -52,8 +52,8 @@ - 96 - 20 + 195 + 60 220 40 @@ -65,8 +65,8 @@ - 96 - 100 + 70 + 180 220 40 @@ -78,8 +78,8 @@ - 96 - 150 + 320 + 180 220 40 @@ -91,8 +91,8 @@ - 96 - 230 + 195 + 120 220 40 @@ -104,8 +104,8 @@ - 96 - 310 + 70 + 300 220 40 @@ -117,8 +117,8 @@ - 96 - 360 + 320 + 300 220 40 @@ -127,30 +127,436 @@ Create all Recursive Scripts + + + + 70 + 240 + 220 + 40 + + + + Create testAuto script + + + + + + 320 + 240 + 220 + 40 + + + + Create all testAuto scripts + + + + + + Windows + + + + + 200 + 130 + 211 + 40 + + + + Hide Windows Taskbar + + + + + + 200 + 200 + 211 + 40 + + + + Show Windows Taskbar + + + + + + Run + + + + false + + + + 10 + 160 + 161 + 28 + + + + Run now + + + + + false + + + + 20 + 240 + 91 + 241 + + + + Days + + + + + 10 + 210 + 80 + 17 + + + + Sunday + + + + + + 10 + 90 + 80 + 17 + + + + Wednesday + + + + + + 10 + 60 + 80 + 17 + + + + Tuesday + + + + + + 10 + 120 + 80 + 17 + + + + Thursday + + + + + + 10 + 150 + 80 + 17 + + + + Friday + + + + + + 10 + 180 + 80 + 17 + + + + Saturday + + + + + + 10 + 30 + 80 + 17 + + + + Monday + + + + + + false + + + + 130 + 240 + 161 + 191 + + + + Times + + + + + 30 + 20 + 118 + 22 + + + + + + + 30 + 60 + 118 + 22 + + + + + + + 30 + 100 + 118 + 22 + + + + + + + 30 + 140 + 118 + 22 + + + + + + + 10 + 23 + 21 + 17 + + + + + + + + + + 10 + 63 + 21 + 17 + + + + + + + + + + 10 + 103 + 21 + 17 + + + + + + + + + + 10 + 143 + 21 + 17 + + + + + + + + + + + 10 + 20 + 161 + 28 + + + + Set Working Folder + + + + + + 190 + 20 + 321 + 31 + + + + (not set...) + + + + + + 300 + 210 + 311 + 331 + + + + + + + 300 + 170 + 41 + 31 + + + + Status: + + + + + + 350 + 170 + 271 + 31 + + + + ####### + + + + + + 20 + 70 + 120 + 20 + + + + <html><head/><body><p>If unchecked, will not show results during evaluation</p></body></html> + + + Server-less + + + false + + + + + + 20 + 100 + 120 + 20 + + + + <html><head/><body><p>If unchecked, will not show results during evaluation</p></body></html> + + + Run Latest + + + true + + + + + + 128 + 95 + 21 + 31 + + + + URL + + + + + false + + + + 160 + 100 + 451 + 21 + + + Evaluate - - - - 90 - 100 - 255 - 23 - - - - 24 - - - 90 - 50 - 131 + 200 + 180 + 120 20 @@ -164,8 +570,8 @@ - 200 - 40 + 330 + 170 101 40 @@ -177,14 +583,14 @@ - TestRail + Web Interface - 180 - 160 - 161 + 240 + 220 + 160 40 @@ -195,8 +601,8 @@ - 80 - 40 + 170 + 100 95 20 @@ -211,9 +617,9 @@ - 180 - 100 - 161 + 240 + 160 + 160 40 @@ -224,9 +630,9 @@ - 180 - 40 - 161 + 240 + 100 + 160 40 @@ -237,8 +643,8 @@ - 80 - 60 + 170 + 120 95 20 @@ -247,45 +653,86 @@ XML - - - - Windows - - + - 100 - 100 - 211 - 40 + 10 + 30 + 601 + 300 - - Hide Windows Taskbar + + TestRail - + - 100 - 170 - 211 - 40 + 10 + 350 + 601 + 151 - - Show Windows Taskbar + + Amazon Web Services + + + true + + + + 240 + 30 + 160 + 40 + + + + Create Web Page + + + + + + 150 + 42 + 81 + 17 + + + + Update AWS + + + + + + 20 + 90 + 561 + 21 + + + + groupBox + updateTestRailRunResultsButton + createPythonScriptRadioButton + createTestRailRunButton + createTestRailTestCasesButton + createXMLScriptRadioButton + groupBox_2 - 110 - 90 - 81 + 120 + 80 + 110 16 @@ -298,32 +745,12 @@ GitHub Branch - - - - 200 - 85 - 140 - 24 - - - - - - - 200 - 47 - 140 - 24 - - - - 110 - 50 - 81 + 120 + 40 + 110 16 @@ -336,13 +763,46 @@ GitHub User + + + + 80 + 760 + 255 + 23 + + + + 24 + + + + + + 220 + 40 + 161 + 21 + + + + + + + 220 + 80 + 161 + 21 + + + 0 0 - 432 + 720 21 @@ -388,6 +848,53 @@ + + userLineEdit + branchLineEdit + createTestsButton + createMDFileButton + createAllMDFilesButton + createTestsOutlineButton + createRecursiveScriptButton + createAllRecursiveScriptsButton + createTestAutoScriptButton + createAllTestAutoScriptsButton + hideTaskbarButton + showTaskbarButton + runNowButton + sundayCheckBox + wednesdayCheckBox + tuesdayCheckBox + thursdayCheckBox + fridayCheckBox + saturdayCheckBox + mondayCheckBox + timeEdit1 + timeEdit2 + timeEdit3 + timeEdit4 + timeEdit1checkBox + timeEdit2checkBox + timeEdit3checkBox + timeEdit4checkBox + setWorkingFolderButton + plainTextEdit + checkBoxServerless + checkBoxRunLatest + urlLineEdit + checkBoxInteractiveMode + evaluateTestsButton + updateTestRailRunResultsButton + createPythonScriptRadioButton + createTestRailRunButton + createTestRailTestCasesButton + createXMLScriptRadioButton + createWebPagePushButton + updateAWSCheckBox + awsURLLineEdit + closeButton + tabWidget + diff --git a/tools/auto-tester/src/ui/MismatchWindow.cpp b/tools/auto-tester/src/ui/MismatchWindow.cpp index 79d2ce9f61..58189b4795 100644 --- a/tools/auto-tester/src/ui/MismatchWindow.cpp +++ b/tools/auto-tester/src/ui/MismatchWindow.cpp @@ -22,6 +22,11 @@ MismatchWindow::MismatchWindow(QWidget *parent) : QDialog(parent) { } QPixmap MismatchWindow::computeDiffPixmap(QImage expectedImage, QImage resultImage) { + // Create an empty difference image if the images differ in size + if (expectedImage.height() != resultImage.height() || expectedImage.width() != resultImage.width()) { + return QPixmap(); + } + // This is an optimization, as QImage.setPixel() is embarrassingly slow unsigned char* buffer = new unsigned char[expectedImage.height() * expectedImage.width() * 3]; @@ -55,20 +60,20 @@ QPixmap MismatchWindow::computeDiffPixmap(QImage expectedImage, QImage resultIma return resultPixmap; } -void MismatchWindow::setTestFailure(TestFailure testFailure) { - errorLabel->setText("Similarity: " + QString::number(testFailure._error)); +void MismatchWindow::setTestResult(TestResult testResult) { + errorLabel->setText("Similarity: " + QString::number(testResult._error)); - imagePath->setText("Path to test: " + testFailure._pathname); + imagePath->setText("Path to test: " + testResult._pathname); - expectedFilename->setText(testFailure._expectedImageFilename); - resultFilename->setText(testFailure._actualImageFilename); + expectedFilename->setText(testResult._expectedImageFilename); + resultFilename->setText(testResult._actualImageFilename); - QPixmap expectedPixmap = QPixmap(testFailure._pathname + testFailure._expectedImageFilename); - QPixmap actualPixmap = QPixmap(testFailure._pathname + testFailure._actualImageFilename); + QPixmap expectedPixmap = QPixmap(testResult._pathname + testResult._expectedImageFilename); + QPixmap actualPixmap = QPixmap(testResult._pathname + testResult._actualImageFilename); _diffPixmap = computeDiffPixmap( - QImage(testFailure._pathname + testFailure._expectedImageFilename), - QImage(testFailure._pathname + testFailure._actualImageFilename) + QImage(testResult._pathname + testResult._expectedImageFilename), + QImage(testResult._pathname + testResult._actualImageFilename) ); expectedImage->setPixmap(expectedPixmap); diff --git a/tools/auto-tester/src/ui/MismatchWindow.h b/tools/auto-tester/src/ui/MismatchWindow.h index f203a2be6a..30c29076b3 100644 --- a/tools/auto-tester/src/ui/MismatchWindow.h +++ b/tools/auto-tester/src/ui/MismatchWindow.h @@ -20,7 +20,7 @@ class MismatchWindow : public QDialog, public Ui::MismatchWindow { public: MismatchWindow(QWidget *parent = Q_NULLPTR); - void setTestFailure(TestFailure testFailure); + void setTestResult(TestResult testResult); UserResponse getUserResponse() { return _userResponse; } diff --git a/tools/auto-tester/src/ui/TestRailResultsSelectorWindow.cpp b/tools/auto-tester/src/ui/TestRailResultsSelectorWindow.cpp index 414e4fca79..505e04b33e 100644 --- a/tools/auto-tester/src/ui/TestRailResultsSelectorWindow.cpp +++ b/tools/auto-tester/src/ui/TestRailResultsSelectorWindow.cpp @@ -25,6 +25,7 @@ void TestRailResultsSelectorWindow::reset() { userLineEdit->setDisabled(false); passwordLineEdit->setDisabled(false); projectIDLineEdit->setDisabled(false); + suiteIDLineEdit->setDisabled(false); OKButton->setDisabled(true); @@ -37,6 +38,7 @@ void TestRailResultsSelectorWindow::on_acceptButton_clicked() { userLineEdit->setDisabled(true); passwordLineEdit->setDisabled(true); projectIDLineEdit->setDisabled(true); + suiteIDLineEdit->setDisabled(true); OKButton->setDisabled(false); diff --git a/tools/auto-tester/src/ui/TestRailRunSelectorWindow.cpp b/tools/auto-tester/src/ui/TestRailRunSelectorWindow.cpp index 54a3985a8b..ac3419d46f 100644 --- a/tools/auto-tester/src/ui/TestRailRunSelectorWindow.cpp +++ b/tools/auto-tester/src/ui/TestRailRunSelectorWindow.cpp @@ -19,12 +19,12 @@ TestRailRunSelectorWindow::TestRailRunSelectorWindow(QWidget *parent) { projectIDLineEdit->setValidator(new QIntValidator(1, 999, this)); } - void TestRailRunSelectorWindow::reset() { urlLineEdit->setDisabled(false); userLineEdit->setDisabled(false); passwordLineEdit->setDisabled(false); projectIDLineEdit->setDisabled(false); + suiteIDLineEdit->setDisabled(false); OKButton->setDisabled(true); sectionsComboBox->setDisabled(true); @@ -35,6 +35,7 @@ void TestRailRunSelectorWindow::on_acceptButton_clicked() { userLineEdit->setDisabled(true); passwordLineEdit->setDisabled(true); projectIDLineEdit->setDisabled(true); + suiteIDLineEdit->setDisabled(true); OKButton->setDisabled(false); sectionsComboBox->setDisabled(false); diff --git a/tools/auto-tester/src/ui/TestRailTestCasesSelectorWindow.cpp b/tools/auto-tester/src/ui/TestRailTestCasesSelectorWindow.cpp index abb873ea14..638fe71819 100644 --- a/tools/auto-tester/src/ui/TestRailTestCasesSelectorWindow.cpp +++ b/tools/auto-tester/src/ui/TestRailTestCasesSelectorWindow.cpp @@ -25,6 +25,7 @@ void TestRailTestCasesSelectorWindow::reset() { userLineEdit->setDisabled(false); passwordLineEdit->setDisabled(false); projectIDLineEdit->setDisabled(false); + suiteIDLineEdit->setDisabled(false); OKButton->setDisabled(true); @@ -37,6 +38,7 @@ void TestRailTestCasesSelectorWindow::on_acceptButton_clicked() { userLineEdit->setDisabled(true); passwordLineEdit->setDisabled(true); projectIDLineEdit->setDisabled(true); + suiteIDLineEdit->setDisabled(true); OKButton->setDisabled(false);