diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index e0c35b7148..e79473783a 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -1042,7 +1043,7 @@ bool AssetServer::loadMappingsFromFile() { bool AssetServer::writeMappingsToFile() { auto mapFilePath = _resourcesDirectory.absoluteFilePath(MAP_FILE_NAME); - QFile mapFile { mapFilePath }; + QSaveFile mapFile { mapFilePath }; if (mapFile.open(QIODevice::WriteOnly)) { QJsonObject root; @@ -1053,8 +1054,12 @@ bool AssetServer::writeMappingsToFile() { QJsonDocument jsonDocument { root }; if (mapFile.write(jsonDocument.toJson()) != -1) { - qCDebug(asset_server) << "Wrote JSON mappings to file at" << mapFilePath; - return true; + if (mapFile.commit()) { + qCDebug(asset_server) << "Wrote JSON mappings to file at" << mapFilePath; + return true; + } else { + qCWarning(asset_server) << "Failed to commit JSON mappings to file at" << mapFilePath; + } } else { qCWarning(asset_server) << "Failed to write JSON mappings to file at" << mapFilePath; } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index d084ca69f9..fd121055a1 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -262,6 +262,26 @@ void MyAvatar::setDominantHand(const QString& hand) { } } +void MyAvatar::requestDisableHandTouch() { + std::lock_guard guard(_disableHandTouchMutex); + _disableHandTouchCount++; + emit shouldDisableHandTouchChanged(_disableHandTouchCount > 0); +} + +void MyAvatar::requestEnableHandTouch() { + std::lock_guard guard(_disableHandTouchMutex); + _disableHandTouchCount = std::max(_disableHandTouchCount - 1, 0); + emit shouldDisableHandTouchChanged(_disableHandTouchCount > 0); +} + +void MyAvatar::disableHandTouchForID(const QUuid& entityID) { + emit disableHandTouchForIDChanged(entityID, true); +} + +void MyAvatar::enableHandTouchForID(const QUuid& entityID) { + emit disableHandTouchForIDChanged(entityID, false); +} + void MyAvatar::registerMetaTypes(ScriptEnginePointer engine) { QScriptValue value = engine->newQObject(this, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); engine->globalObject().setProperty("MyAvatar", value); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 819d5b0066..9b5ddd360d 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -505,6 +505,28 @@ public: * @returns {boolean} */ Q_INVOKABLE bool getHMDLeanRecenterEnabled() const { return _hmdLeanRecenterEnabled; } + /**jsdoc + * Request to enable hand touch effect globally + * @function MyAvatar.requestEnableHandTouch + */ + Q_INVOKABLE void requestEnableHandTouch(); + /**jsdoc + * Request to disable hand touch effect globally + * @function MyAvatar.requestDisableHandTouch + */ + Q_INVOKABLE void requestDisableHandTouch(); + /**jsdoc + * Disables hand touch effect on a specific entity + * @function MyAvatar.disableHandTouchForID + * @param {Uuid} entityID - ID of the entity that will disable hand touch effect + */ + Q_INVOKABLE void disableHandTouchForID(const QUuid& entityID); + /**jsdoc + * Enables hand touch effect on a specific entity + * @function MyAvatar.enableHandTouchForID + * @param {Uuid} entityID - ID of the entity that will enable hand touch effect + */ + Q_INVOKABLE void enableHandTouchForID(const QUuid& entityID); bool useAdvancedMovementControls() const { return _useAdvancedMovementControls.get(); } void setUseAdvancedMovementControls(bool useAdvancedMovementControls) @@ -1392,6 +1414,23 @@ signals: */ void scaleChanged(); + /**jsdoc + * Triggered when hand touch is globally enabled or disabled + * @function MyAvatar.shouldDisableHandTouchChanged + * @param {boolean} shouldDisable + * @returns {Signal} + */ + void shouldDisableHandTouchChanged(bool shouldDisable); + + /**jsdoc + * Triggered when hand touch is enabled or disabled for an specific entity + * @function MyAvatar.disableHandTouchForIDChanged + * @param {Uuid} entityID - ID of the entity that will enable hand touch effect + * @param {boolean} disable + * @returns {Signal} + */ + void disableHandTouchForIDChanged(const QUuid& entityID, bool disable); + private slots: void leaveDomain(); @@ -1628,6 +1667,7 @@ private: // all poses are in sensor-frame std::map _controllerPoseMap; mutable std::mutex _controllerPoseMapMutex; + mutable std::mutex _disableHandTouchMutex; bool _centerOfGravityModelEnabled { true }; bool _hmdLeanRecenterEnabled { true }; @@ -1668,6 +1708,7 @@ private: bool _shouldLoadScripts { false }; bool _haveReceivedHeightLimitsFromDomain { false }; + int _disableHandTouchCount { 0 }; }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); diff --git a/libraries/fbx/src/GLTFReader.cpp b/libraries/fbx/src/GLTFReader.cpp index 1fa4b3873e..300e6f4846 100644 --- a/libraries/fbx/src/GLTFReader.cpp +++ b/libraries/fbx/src/GLTFReader.cpp @@ -21,14 +21,17 @@ #include #include + #include #include #include +#include #include #include #include +#include #include "FBXReader.h" @@ -786,13 +789,18 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { QVector raw_vertices; QVector raw_normals; - addArrayOfType(indicesBuffer.blob, + bool success = addArrayOfType(indicesBuffer.blob, indicesBufferview.byteOffset + indicesAccBoffset, - indicesBufferview.byteLength, + indicesAccessor.count, part.triangleIndices, indicesAccessor.type, indicesAccessor.componentType); + if (!success) { + qWarning(modelformat) << "There was a problem reading glTF INDICES data for model " << _url; + continue; + } + QList keys = primitive.attributes.values.keys(); foreach(auto &key, keys) { @@ -805,44 +813,60 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { int accBoffset = accessor.defined["byteOffset"] ? accessor.byteOffset : 0; if (key == "POSITION") { QVector vertices; - addArrayOfType(buffer.blob, + success = addArrayOfType(buffer.blob, bufferview.byteOffset + accBoffset, - bufferview.byteLength, vertices, + accessor.count, vertices, accessor.type, accessor.componentType); + if (!success) { + qWarning(modelformat) << "There was a problem reading glTF POSITION data for model " << _url; + continue; + } for (int n = 0; n < vertices.size(); n = n + 3) { mesh.vertices.push_back(glm::vec3(vertices[n], vertices[n + 1], vertices[n + 2])); } } else if (key == "NORMAL") { QVector normals; - addArrayOfType(buffer.blob, + success = addArrayOfType(buffer.blob, bufferview.byteOffset + accBoffset, - bufferview.byteLength, + accessor.count, normals, accessor.type, accessor.componentType); + if (!success) { + qWarning(modelformat) << "There was a problem reading glTF NORMAL data for model " << _url; + continue; + } for (int n = 0; n < normals.size(); n = n + 3) { mesh.normals.push_back(glm::vec3(normals[n], normals[n + 1], normals[n + 2])); } } else if (key == "TEXCOORD_0") { QVector texcoords; - addArrayOfType(buffer.blob, + success = addArrayOfType(buffer.blob, bufferview.byteOffset + accBoffset, - bufferview.byteLength, + accessor.count, texcoords, accessor.type, accessor.componentType); + if (!success) { + qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_0 data for model " << _url; + continue; + } for (int n = 0; n < texcoords.size(); n = n + 2) { mesh.texCoords.push_back(glm::vec2(texcoords[n], texcoords[n + 1])); } } else if (key == "TEXCOORD_1") { QVector texcoords; - addArrayOfType(buffer.blob, + success = addArrayOfType(buffer.blob, bufferview.byteOffset + accBoffset, - bufferview.byteLength, + accessor.count, texcoords, accessor.type, accessor.componentType); + if (!success) { + qWarning(modelformat) << "There was a problem reading glTF TEXCOORD_1 data for model " << _url; + continue; + } for (int n = 0; n < texcoords.size(); n = n + 2) { mesh.texCoords1.push_back(glm::vec2(texcoords[n], texcoords[n + 1])); } @@ -888,8 +912,16 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { FBXGeometry* GLTFReader::readGLTF(QByteArray& model, const QVariantHash& mapping, const QUrl& url, bool loadLightmaps, float lightmapLevel) { + _url = url; + // Normalize url for local files + QUrl normalizeUrl = DependencyManager::get()->normalizeURL(url); + if (normalizeUrl.scheme().isEmpty() || (normalizeUrl.scheme() == "file")) { + QString localFileName = PathUtils::expandToLocalDataAbsolutePath(normalizeUrl).toLocalFile(); + _url = QUrl(QFileInfo(localFileName).absoluteFilePath()); + } + parseGLTF(model); //_file.dump(); FBXGeometry* geometryPtr = new FBXGeometry(); @@ -904,6 +936,7 @@ FBXGeometry* GLTFReader::readGLTF(QByteArray& model, const QVariantHash& mapping bool GLTFReader::readBinary(const QString& url, QByteArray& outdata) { QUrl binaryUrl = _url.resolved(QUrl(url).fileName()); + qCDebug(modelformat) << "binaryUrl: " << binaryUrl << " OriginalUrl: " << _url; bool success; std::tie(success, outdata) = requestData(binaryUrl); @@ -1018,13 +1051,12 @@ void GLTFReader::setFBXMaterial(FBXMaterial& fbxmat, const GLTFMaterial& materia fbxmat.opacityTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]); fbxmat.albedoTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]); fbxmat.useAlbedoMap = true; - fbxmat.metallicTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.baseColorTexture]); - fbxmat.useMetallicMap = true; } if (material.pbrMetallicRoughness.defined["metallicRoughnessTexture"]) { fbxmat.roughnessTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]); fbxmat.useRoughnessMap = true; - + fbxmat.metallicTexture = getFBXTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]); + fbxmat.useMetallicMap = true; } if (material.pbrMetallicRoughness.defined["roughnessFactor"]) { fbxmat._material->setRoughness(material.pbrMetallicRoughness.roughnessFactor); @@ -1043,7 +1075,7 @@ void GLTFReader::setFBXMaterial(FBXMaterial& fbxmat, const GLTFMaterial& materia } template -bool GLTFReader::readArray(const QByteArray& bin, int byteOffset, int byteLength, +bool GLTFReader::readArray(const QByteArray& bin, int byteOffset, int count, QVector& outarray, int accessorType) { QDataStream blobstream(bin); @@ -1051,142 +1083,77 @@ bool GLTFReader::readArray(const QByteArray& bin, int byteOffset, int byteLength blobstream.setVersion(QDataStream::Qt_5_9); blobstream.setFloatingPointPrecision(QDataStream::FloatingPointPrecision::SinglePrecision); - int vsize = byteLength / sizeof(T); - - qCDebug(modelformat) << "size1: " << vsize; + qCDebug(modelformat) << "size1: " << count; int dataskipped = blobstream.skipRawData(byteOffset); qCDebug(modelformat) << "dataskipped: " << dataskipped; - - while (outarray.size() < vsize) { - - T value1, value2, value3, value4, - value5, value6, value7, value8, - value9, value10, value11, value12, - value13, value14, value15, value16; - - if (accessorType == GLTFAccessorType::SCALAR) { - - blobstream >> value1; - - outarray.push_back(value1); - } else if (accessorType == GLTFAccessorType::VEC2) { - - blobstream >> value1; - blobstream >> value2; - - outarray.push_back(value1); - outarray.push_back(value2); - } else if (accessorType == GLTFAccessorType::VEC3) { - - blobstream >> value1; - blobstream >> value2; - blobstream >> value3; - - outarray.push_back(value1); - outarray.push_back(value2); - outarray.push_back(value3); - } else if (accessorType == GLTFAccessorType::VEC4 || accessorType == GLTFAccessorType::MAT2) { - - blobstream >> value1; - blobstream >> value2; - blobstream >> value3; - blobstream >> value4; - - outarray.push_back(value1); - outarray.push_back(value2); - outarray.push_back(value3); - outarray.push_back(value4); - } else if (accessorType == GLTFAccessorType::MAT3) { - - blobstream >> value1; - blobstream >> value2; - blobstream >> value3; - blobstream >> value4; - blobstream >> value5; - blobstream >> value6; - blobstream >> value7; - blobstream >> value8; - blobstream >> value9; - - outarray.push_back(value1); - outarray.push_back(value2); - outarray.push_back(value3); - outarray.push_back(value4); - outarray.push_back(value5); - outarray.push_back(value6); - outarray.push_back(value7); - outarray.push_back(value8); - outarray.push_back(value9); - } else if (accessorType == GLTFAccessorType::MAT4) { - - blobstream >> value1; - blobstream >> value2; - blobstream >> value3; - blobstream >> value4; - blobstream >> value5; - blobstream >> value6; - blobstream >> value7; - blobstream >> value8; - blobstream >> value9; - blobstream >> value10; - blobstream >> value11; - blobstream >> value12; - blobstream >> value13; - blobstream >> value14; - blobstream >> value15; - blobstream >> value16; - - outarray.push_back(value1); - outarray.push_back(value2); - outarray.push_back(value3); - outarray.push_back(value4); - outarray.push_back(value5); - outarray.push_back(value6); - outarray.push_back(value7); - outarray.push_back(value8); - outarray.push_back(value9); - outarray.push_back(value10); - outarray.push_back(value11); - outarray.push_back(value12); - outarray.push_back(value13); - outarray.push_back(value14); - outarray.push_back(value15); - outarray.push_back(value16); - + int bufferCount = 0; + switch (accessorType) { + case GLTFAccessorType::SCALAR: + bufferCount = 1; + break; + case GLTFAccessorType::VEC2: + bufferCount = 2; + break; + case GLTFAccessorType::VEC3: + bufferCount = 3; + break; + case GLTFAccessorType::VEC4: + bufferCount = 4; + break; + case GLTFAccessorType::MAT2: + bufferCount = 4; + break; + case GLTFAccessorType::MAT3: + bufferCount = 9; + break; + case GLTFAccessorType::MAT4: + bufferCount = 16; + break; + default: + qWarning(modelformat) << "Unknown accessorType: " << accessorType; + blobstream.unsetDevice(); + return false; + } + for (int i = 0; i < count; i++) { + for (int j = 0; j < bufferCount; j++) { + if (!blobstream.atEnd()) { + T value; + blobstream >> value; + outarray.push_back(value); + } else { + blobstream.unsetDevice(); + return false; + } } } + blobstream.unsetDevice(); return true; } template -bool GLTFReader::addArrayOfType(const QByteArray& bin, int byteOffset, int byteLength, +bool GLTFReader::addArrayOfType(const QByteArray& bin, int byteOffset, int count, QVector& outarray, int accessorType, int componentType) { switch (componentType) { case GLTFAccessorComponentType::BYTE: {} case GLTFAccessorComponentType::UNSIGNED_BYTE: { - readArray(bin, byteOffset, byteLength, outarray, accessorType); - break; + return readArray(bin, byteOffset, count, outarray, accessorType); } case GLTFAccessorComponentType::SHORT: { - readArray(bin, byteOffset, byteLength, outarray, accessorType); - break; + return readArray(bin, byteOffset, count, outarray, accessorType); } case GLTFAccessorComponentType::UNSIGNED_INT: { - readArray(bin, byteOffset, byteLength, outarray, accessorType); - break; + return readArray(bin, byteOffset, count, outarray, accessorType); } case GLTFAccessorComponentType::UNSIGNED_SHORT: { - readArray(bin, byteOffset, byteLength, outarray, accessorType); - break; + return readArray(bin, byteOffset, count, outarray, accessorType); } case GLTFAccessorComponentType::FLOAT: { - readArray(bin, byteOffset, byteLength, outarray, accessorType); - break; + return readArray(bin, byteOffset, count, outarray, accessorType); } } - return true; + return false; } void GLTFReader::retriangulate(const QVector& inIndices, const QVector& in_vertices, diff --git a/libraries/fbx/src/GLTFReader.h b/libraries/fbx/src/GLTFReader.h index 28c1d8282f..2183256b87 100644 --- a/libraries/fbx/src/GLTFReader.h +++ b/libraries/fbx/src/GLTFReader.h @@ -762,11 +762,11 @@ private: bool readBinary(const QString& url, QByteArray& outdata); template - bool readArray(const QByteArray& bin, int byteOffset, int byteLength, + bool readArray(const QByteArray& bin, int byteOffset, int count, QVector& outarray, int accessorType); template - bool addArrayOfType(const QByteArray& bin, int byteOffset, int byteLength, + bool addArrayOfType(const QByteArray& bin, int byteOffset, int count, QVector& outarray, int accessorType, int componentType); void retriangulate(const QVector& in_indices, const QVector& in_vertices, diff --git a/scripts/system/controllers/handTouch.js b/scripts/system/controllers/handTouch.js index 5e633d4740..db79aa4a77 100644 --- a/scripts/system/controllers/handTouch.js +++ b/scripts/system/controllers/handTouch.js @@ -14,64 +14,64 @@ /* global Script, Overlays, Controller, Vec3, MyAvatar, Entities */ -(function() { - - var MSECONDS_AFTER_LOAD = 2000; +(function () { + var handTouchEnabled = true; + 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 + + // Additionally close the hands to achieve a grabbing effect var grabPercent = { left: 0, right: 0 }; - + var Palm = function() { this.position = {x: 0, y: 0, z: 0}; this.perpendicular = {x: 0, y: 0, z: 0}; this.distance = 0; this.fingers = { - pinky: {x: 0, y: 0, z: 0}, - middle: {x: 0, y: 0, z: 0}, - ring: {x: 0, y: 0, z: 0}, - thumb: {x: 0, y: 0, z: 0}, + pinky: {x: 0, y: 0, z: 0}, + middle: {x: 0, y: 0, z: 0}, + ring: {x: 0, y: 0, z: 0}, + thumb: {x: 0, y: 0, z: 0}, index: {x: 0, y: 0, z: 0} }; this.set = false; }; - + var palmData = { left: new Palm(), right: new Palm() }; var handJointNames = {left: "LeftHand", right: "RightHand"}; - - // Store which fingers are touching - if all false restate the default poses + + // Store which fingers are touching - if all false restate the default poses var isTouching = { left: { - pinky: false, - middle: false, - ring: false, - thumb: false, - index: false + pinky: false, + middle: false, + ring: false, + thumb: false, + index: false }, right: { - pinky: false, - middle: false, - ring: false, - thumb: false, + pinky: false, + middle: false, + ring: false, + thumb: false, index: false } }; - + // frame count for transition to default pose - + var countToDefault = { left: 0, right: 0 }; - + // joint data for open pose var dataOpen = { left: { @@ -128,7 +128,7 @@ ] } }; - + // joint data for close pose var dataClose = { left: { @@ -185,78 +185,78 @@ ] } }; - + // snapshot for the default pose var dataDefault = { left: { - pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], - middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], - ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], - thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], index: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], set: false }, right: { - pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], - middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], - ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], - thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], index: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], set: false } }; - + // joint data for the current frame var dataCurrent = { left: { - pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], - middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], - ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], - thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], index: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}] }, right: { - pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], - middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], - ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], - thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], index: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}] } }; - - // interpolated values on joint data to smooth movement + + // interpolated values on joint data to smooth movement var dataDelta = { left: { - pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], - middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], - ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], - thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], index: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}] }, right: { - pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], - middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], - ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], - thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + pinky: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + middle: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + ring: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], + thumb: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}], index: [{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0},{x: 0, y: 0, z: 0, w: 0}] } }; - + // Acquire an updated value per hand every 5 frames when finger is touching (faster in) var touchAnimationSteps = 5; - - // Acquire an updated value per hand every 20 frames when finger is returning to default position (slower out) + + // Acquire an updated value per hand every 20 frames when finger is returning to default position (slower out) var defaultAnimationSteps = 10; - + // Debugging info var showSphere = false; var showLines = false; - + // This get setup on creation var linesCreated = false; var sphereCreated = false; - - // Register object with API Debugger + + // Register object with API Debugger var varsToDebug = { scriptLoaded: false, toggleDebugSphere: function() { @@ -275,17 +275,17 @@ }, fingerPercent: { left: { - pinky: 0.38, - middle: 0.38, - ring: 0.38, - thumb: 0.38, + pinky: 0.38, + middle: 0.38, + ring: 0.38, + thumb: 0.38, index: 0.38 - } , + } , right: { - pinky: 0.38, - middle: 0.38, - ring: 0.38, - thumb: 0.38, + pinky: 0.38, + middle: 0.38, + ring: 0.38, + thumb: 0.38, index: 0.38 } }, @@ -300,12 +300,11 @@ palmData: { left: new Palm(), right: new Palm() - }, + }, offset: {x: 0, y: 0, z: 0}, avatarLoaded: false }; - - + // Add/Subtract the joint data - per finger joint function addVals(val1, val2, sign) { var val = []; @@ -321,7 +320,7 @@ } return val; } - + // Multiply/Divide the joint data - per finger joint function multiplyValsBy(val1, num) { var val = []; @@ -334,7 +333,7 @@ } return val; } - + // Calculate the finger lengths by adding its joint lengths function getJointDistances(jointNamesArray) { var result = {distances: [], totalDistance: 0}; @@ -349,13 +348,12 @@ } return result; } - - function dataRelativeToWorld(side, dataIn, dataOut) { + function dataRelativeToWorld(side, dataIn, dataOut) { var handJoint = handJointNames[side]; var jointIndex = MyAvatar.getJointIndex(handJoint); var worldPosHand = MyAvatar.jointToWorldPoint({x: 0, y: 0, z: 0}, jointIndex); - + dataOut.position = MyAvatar.jointToWorldPoint(dataIn.position, jointIndex); var localPerpendicular = side === "right" ? {x: 0.2, y: 0, z: 1} : {x: -0.2, y: 0, z: 1}; dataOut.perpendicular = Vec3.normalize( @@ -365,15 +363,14 @@ for (var i = 0; i < fingerKeys.length; i++) { var finger = fingerKeys[i]; dataOut.fingers[finger] = MyAvatar.jointToWorldPoint(dataIn.fingers[finger], jointIndex); - } + } } - - function dataRelativeToHandJoint(side, dataIn, dataOut) { + function dataRelativeToHandJoint(side, dataIn, dataOut) { var handJoint = handJointNames[side]; var jointIndex = MyAvatar.getJointIndex(handJoint); var worldPosHand = MyAvatar.jointToWorldPoint({x: 0, y: 0, z: 0}, jointIndex); - + dataOut.position = MyAvatar.worldToJointPoint(dataIn.position, jointIndex); dataOut.perpendicular = MyAvatar.worldToJointPoint(Vec3.sum(worldPosHand, dataIn.perpendicular), jointIndex); dataOut.distance = dataIn.distance; @@ -382,46 +379,44 @@ dataOut.fingers[finger] = MyAvatar.worldToJointPoint(dataIn.fingers[finger], jointIndex); } } - - // Calculate touch field; Sphere at the center of the palm, + + // Calculate touch field; Sphere at the center of the palm, // perpendicular vector from the palm plane and origin of the the finger rays - function estimatePalmData(side) { // Return data object - var data = new Palm(); - - var jointOffset = { x: 0, y: 0, z: 0 }; - + var data = new Palm(); + + var jointOffset = { x: 0, y: 0, z: 0 }; + var upperSide = side[0].toUpperCase() + side.substring(1); var jointIndexHand = MyAvatar.getJointIndex(upperSide + "Hand"); - + // Store position of the hand joint var worldPosHand = MyAvatar.jointToWorldPoint(jointOffset, jointIndexHand); var minusWorldPosHand = {x: -worldPosHand.x, y: -worldPosHand.y, z: -worldPosHand.z}; - + // Data for finger rays var directions = {pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined}; var positions = {pinky: undefined, middle: undefined, ring: undefined, thumb: undefined, index: undefined}; - + var thumbLength = 0; var weightCount = 0; - + // Calculate palm center - var handJointWeight = 1; var fingerJointWeight = 2; - + var palmCenter = {x: 0, y: 0, z: 0}; palmCenter = Vec3.sum(worldPosHand, palmCenter); - + weightCount += handJointWeight; - + for (var i = 0; i < fingerKeys.length; i++) { var finger = fingerKeys[i]; var jointSuffixes = 4; // Get 4 joint names with suffix numbers (0, 1, 2, 3) var jointNames = getJointNames(side, finger, jointSuffixes); var fingerLength = getJointDistances(jointNames).totalDistance; - + var jointIndex = MyAvatar.getJointIndex(jointNames[0]); positions[finger] = MyAvatar.jointToWorldPoint(jointOffset, jointIndex); directions[finger] = Vec3.normalize(Vec3.sum(positions[finger], minusWorldPosHand)); @@ -429,66 +424,63 @@ if (finger !== "thumb") { // finger joints have double the weight than the hand joint // This would better position the palm estimation - - palmCenter = Vec3.sum(Vec3.multiply(fingerJointWeight, positions[finger]), palmCenter); + + palmCenter = Vec3.sum(Vec3.multiply(fingerJointWeight, positions[finger]), palmCenter); weightCount += fingerJointWeight; } else { thumbLength = fingerLength; } } - + // perpendicular change direction depending on the side - data.perpendicular = (side === "right") ? - Vec3.normalize(Vec3.cross(directions.index, directions.pinky)): + data.perpendicular = (side === "right") ? + Vec3.normalize(Vec3.cross(directions.index, directions.pinky)): Vec3.normalize(Vec3.cross(directions.pinky, directions.index)); - + data.position = Vec3.multiply(1.0/weightCount, palmCenter); - + if (side === "right") { varsToDebug.offset = MyAvatar.worldToJointPoint(worldPosHand, jointIndexHand); } - + var palmDistanceMultiplier = 1.55; // 1.55 based on test/error for the sphere radius that best fits the hand - data.distance = palmDistanceMultiplier*Vec3.distance(data.position, positions.index); + data.distance = palmDistanceMultiplier*Vec3.distance(data.position, positions.index); // move back thumb ray origin var thumbBackMultiplier = 0.2; data.fingers.thumb = Vec3.sum( data.fingers.thumb, Vec3.multiply( -thumbBackMultiplier * thumbLength, data.perpendicular)); - + // return getDataRelativeToHandJoint(side, data); dataRelativeToHandJoint(side, data, palmData[side]); palmData[side].set = true; - // return palmData[side]; } - + // Register GlobalDebugger for API Debugger Script.registerValue("GlobalDebugger", varsToDebug); // store the rays for the fingers - only for debug purposes - var fingerRays = { + var fingerRays = { left: { - pinky: undefined, - middle: undefined, - ring: undefined, - thumb: undefined, + pinky: undefined, + middle: undefined, + ring: undefined, + thumb: undefined, index: undefined - }, + }, right: { - pinky: undefined, - middle: undefined, - ring: undefined, - thumb: undefined, + pinky: undefined, + middle: undefined, + ring: undefined, + thumb: undefined, index: undefined } }; - + // Create debug overlays - finger rays + palm rays + spheres - var palmRay, sphereHand; - + function createDebugLines() { - for (var i = 0; i < fingerKeys.length; i++) { fingerRays.left[fingerKeys[i]] = Overlays.addOverlay("line3d", { color: { red: 0, green: 0, blue: 255 }, @@ -503,7 +495,7 @@ visible: showLines }); } - + palmRay = { left: Overlays.addOverlay("line3d", { color: { red: 255, green: 0, blue: 0 }, @@ -520,9 +512,8 @@ }; linesCreated = true; } - + function createDebugSphere() { - sphereHand = { right: Overlays.addOverlay("sphere", { position: MyAvatar.position, @@ -536,10 +527,10 @@ scale: { x: 0.01, y: 0.01, z: 0.01 }, visible: showSphere }) - }; + }; sphereCreated = true; } - + function acquireDefaultPose(side) { for (var i = 0; i < fingerKeys.length; i++) { var finger = fingerKeys[i]; @@ -553,86 +544,87 @@ } dataDefault[side].set = true; } - - var rayPicks = { - left: { - pinky: undefined, - middle: undefined, - ring: undefined, - thumb: undefined, + + var rayPicks = { + left: { + pinky: undefined, + middle: undefined, + ring: undefined, + thumb: undefined, index: undefined }, - right: { - pinky: undefined, - middle: undefined, - ring: undefined, - thumb: undefined, + right: { + pinky: undefined, + middle: undefined, + ring: undefined, + thumb: undefined, index: undefined } }; - - var dataFailed = { - left: { - pinky: 0, - middle: 0, - ring: 0, - thumb: 0, + + var dataFailed = { + left: { + pinky: 0, + middle: 0, + ring: 0, + thumb: 0, index: 0 }, - right: { - pinky: 0, - middle: 0, - ring: 0, - thumb: 0, + right: { + pinky: 0, + middle: 0, + ring: 0, + thumb: 0, index: 0 } }; - + function clearRayPicks(side) { for (var i = 0; i < fingerKeys.length; i++) { var finger = fingerKeys[i]; if (rayPicks[side][finger] !== undefined) { RayPick.removeRayPick(rayPicks[side][finger]); rayPicks[side][finger] = undefined; - } + } } } - + function createRayPicks(side) { var data = palmData[side]; clearRayPicks(side); for (var i = 0; i < fingerKeys.length; i++) { - var finger = fingerKeys[i]; + var finger = fingerKeys[i]; var LOOKUP_DISTANCE_MULTIPLIER = 1.5; var dist = LOOKUP_DISTANCE_MULTIPLIER*data.distance; - var checkOffset = { - x: data.perpendicular.x * dist, - y: data.perpendicular.y * dist, - z: data.perpendicular.z * dist + var checkOffset = { + x: data.perpendicular.x * dist, + y: data.perpendicular.y * dist, + z: data.perpendicular.z * dist }; - + var checkPoint = Vec3.sum(data.position, Vec3.multiply(2, checkOffset)); var sensorToWorldScale = MyAvatar.getSensorToWorldScale(); - + var origin = data.fingers[finger]; - + var direction = Vec3.normalize(Vec3.subtract(checkPoint, origin)); - + origin = Vec3.multiply(1/sensorToWorldScale, origin); - + rayPicks[side][finger] = RayPick.createRayPick( - { - "enabled": false, + { + "enabled": false, "joint": handJointNames[side], "posOffset": origin, "dirOffset": direction, "filter": RayPick.PICK_ENTITIES } - ); - - RayPick.setPrecisionPicking(rayPicks[side][finger], true); - } + ); + + RayPick.setPrecisionPicking(rayPicks[side][finger], true); + } } + function activateNextRay(side, index) { var nextIndex = (index < fingerKeys.length-1) ? index + 1 : 0; for (var i = 0; i < fingerKeys.length; i++) { @@ -641,46 +633,44 @@ RayPick.enableRayPick(rayPicks[side][finger]); } else { RayPick.disableRayPick(rayPicks[side][finger]); - } + } } } - - function updateSphereHand(side) { + function updateSphereHand(side) { var data = new Palm(); dataRelativeToWorld(side, palmData[side], data); varsToDebug.palmData[side] = palmData[side]; - + var palmPoint = data.position; var LOOKUP_DISTANCE_MULTIPLIER = 1.5; var dist = LOOKUP_DISTANCE_MULTIPLIER*data.distance; - - // Situate the debugging overlays - - var checkOffset = { - x: data.perpendicular.x * dist, - y: data.perpendicular.y * dist, - z: data.perpendicular.z * dist + + // Situate the debugging overlays + var checkOffset = { + x: data.perpendicular.x * dist, + y: data.perpendicular.y * dist, + z: data.perpendicular.z * dist }; - + var spherePos = Vec3.sum(palmPoint, checkOffset); var checkPoint = Vec3.sum(palmPoint, Vec3.multiply(2, checkOffset)); - + if (showLines) { Overlays.editOverlay(palmRay[side], { start: palmPoint, end: checkPoint, visible: showLines - }); + }); for (var i = 0; i < fingerKeys.length; i++) { Overlays.editOverlay(fingerRays[side][fingerKeys[i]], { start: data.fingers[fingerKeys[i]], end: checkPoint, visible: showLines }); - } + } } - + if (showSphere) { Overlays.editOverlay(sphereHand[side], { position: spherePos, @@ -690,16 +680,16 @@ z: 2*dist }, visible: showSphere - }); + }); } - + // Update the intersection of only one finger at a time - - var finger = fingerKeys[updateFingerWithIndex]; - - - var grabbables = Entities.findEntities(spherePos, dist); - + var finger = fingerKeys[updateFingerWithIndex]; + var nearbyEntities = Entities.findEntities(spherePos, dist); + // Filter the entities that are allowed to be touched + var touchableEntities = nearbyEntities.filter(function (id) { + return untouchableEntities.indexOf(id) == -1; + }); var intersection; if (rayPicks[side][finger] !== undefined) { intersection = RayPick.getPrevRayPickResult(rayPicks[side][finger]); @@ -708,11 +698,10 @@ var animationSteps = defaultAnimationSteps; var newFingerData = dataDefault[side][finger]; var isAbleToGrab = false; - if (grabbables.length > 0) { - - RayPick.setIncludeItems(rayPicks[side][finger], grabbables); + if (touchableEntities.length > 0) { + RayPick.setIncludeItems(rayPicks[side][finger], touchableEntities); - if (intersection === undefined) { + if (intersection === undefined) { return; } @@ -725,28 +714,27 @@ // Store if this finger is touching something isTouching[side][finger] = isAbleToGrab; if (isAbleToGrab) { - // update the open/close percentage for this finger - + // update the open/close percentage for this finger var FINGER_REACT_MULTIPLIER = 2.8; - + percent = intersection.distance/(FINGER_REACT_MULTIPLIER*dist); - + var THUMB_FACTOR = 0.2; var FINGER_FACTOR = 0.05; - + // Amount of grab coefficient added to the fingers - thumb is higher - var grabMultiplier = finger === "thumb" ? THUMB_FACTOR : FINGER_FACTOR; + var grabMultiplier = finger === "thumb" ? THUMB_FACTOR : FINGER_FACTOR; percent += grabMultiplier * grabPercent[side]; - + // Calculate new interpolation data var totalDistance = addVals(dataClose[side][finger], dataOpen[side][finger], -1); // Assign close/open ratio to finger to simulate touch - newFingerData = addVals(dataOpen[side][finger], multiplyValsBy(totalDistance, percent), 1); + newFingerData = addVals(dataOpen[side][finger], multiplyValsBy(totalDistance, percent), 1); animationSteps = touchAnimationSteps; - } + } varsToDebug.fingerPercent[side][finger] = percent; - - } + + } if (!isAbleToGrab) { dataFailed[side][finger] = dataFailed[side][finger] === 0 ? 1 : 2; } else { @@ -755,13 +743,12 @@ // If it only fails once it will not update increments if (dataFailed[side][finger] !== 1) { // Calculate animation increments - dataDelta[side][finger] = - multiplyValsBy(addVals(newFingerData, dataCurrent[side][finger], -1), 1.0/animationSteps); + dataDelta[side][finger] = + multiplyValsBy(addVals(newFingerData, dataCurrent[side][finger], -1), 1.0/animationSteps); } } - + // Recreate the finger joint names - function getJointNames(side, finger, count) { var names = []; for (var i = 1; i < count+1; i++) { @@ -772,30 +759,34 @@ } // Capture the controller values - var leftTriggerPress = function (value) { varsToDebug.triggerValues.leftTriggerValue = value; - // the value for the trigger increments the hand-close percentage + // the value for the trigger increments the hand-close percentage grabPercent.left = value; }; + var leftTriggerClick = function (value) { varsToDebug.triggerValues.leftTriggerClicked = value; }; + var rightTriggerPress = function (value) { varsToDebug.triggerValues.rightTriggerValue = value; - // the value for the trigger increments the hand-close percentage + // the value for the trigger increments the hand-close percentage grabPercent.right = value; }; + var rightTriggerClick = function (value) { varsToDebug.triggerValues.rightTriggerClicked = value; }; + var leftSecondaryPress = function (value) { varsToDebug.triggerValues.leftSecondaryValue = value; }; + var rightSecondaryPress = function (value) { varsToDebug.triggerValues.rightSecondaryValue = value; }; - + var MAPPING_NAME = "com.highfidelity.handTouch"; var mapping = Controller.newMapping(MAPPING_NAME); mapping.from([Controller.Standard.RT]).peek().to(rightTriggerPress); @@ -809,16 +800,17 @@ mapping.from([Controller.Standard.RightGrip]).peek().to(rightSecondaryPress); Controller.enableMapping(MAPPING_NAME); - + if (showLines && !linesCreated) { createDebugLines(); linesCreated = true; } + if (showSphere && !sphereCreated) { createDebugSphere(); sphereCreated = true; } - + function getTouching(side) { var animating = false; for (var i = 0; i < fingerKeys.length; i++) { @@ -827,19 +819,70 @@ } return animating; // return false only if none of the fingers are touching } - + function reEstimatePalmData() { ["right", "left"].forEach(function(side) { estimatePalmData(side); }); } - + function recreateRayPicks() { ["right", "left"].forEach(function(side) { createRayPicks(side); }); } - + + function cleanUp() { + ["right", "left"].forEach(function (side) { + if (linesCreated) { + Overlays.deleteOverlay(palmRay[side]); + } + if (sphereCreated) { + Overlays.deleteOverlay(sphereHand[side]); + } + clearRayPicks(side); + for (var i = 0; i < fingerKeys.length; i++) { + var finger = fingerKeys[i]; + var jointSuffixes = 3; // We need to clear the joints 0, 1 and 2 joints + var names = getJointNames(side, finger, jointSuffixes); + for (var j = 0; j < names.length; j++) { + var index = MyAvatar.getJointIndex(names[j]); + MyAvatar.clearJointData(index); + } + if (linesCreated) { + Overlays.deleteOverlay(fingerRays[side][finger]); + } + } + }); + } + + MyAvatar.shouldDisableHandTouchChanged.connect(function (shouldDisable) { + if (shouldDisable) { + if (handTouchEnabled) { + cleanUp(); + } + } else { + if (!handTouchEnabled) { + reEstimatePalmData(); + recreateRayPicks(); + } + } + handTouchEnabled = !shouldDisable; + }); + + MyAvatar.disableHandTouchForIDChanged.connect(function (entityID, disable) { + var entityIndex = untouchableEntities.indexOf(entityID); + if (disable) { + if (entityIndex == -1) { + untouchableEntities.push(entityID); + } + } else { + if (entityIndex != -1) { + untouchableEntities.splice(entityIndex, 1); + } + } + }); + MyAvatar.onLoadComplete.connect(function () { // Sometimes the rig is not ready when this signal is trigger console.log("avatar loaded"); @@ -848,78 +891,55 @@ recreateRayPicks(); }, MSECONDS_AFTER_LOAD); }); - + MyAvatar.sensorToWorldScaleChanged.connect(function() { reEstimatePalmData(); }); - - Script.scriptEnding.connect(function () { - ["right", "left"].forEach(function(side) { - if (linesCreated) { - Overlays.deleteOverlay(palmRay[side]); - } - if (sphereCreated) { - Overlays.deleteOverlay(sphereHand[side]); - } - clearRayPicks(side); - for (var i = 0; i < fingerKeys.length; i++) { - var finger = fingerKeys[i]; - var jointSuffixes = 3; // We need to clear the joints 0, 1 and 2 joints - var names = getJointNames(side, finger, jointSuffixes); - - for (var j = 0; j < names.length; j++) { - var index = MyAvatar.getJointIndex(names[j]); - MyAvatar.clearJointData(index); - } - - if (linesCreated) { - Overlays.deleteOverlay(fingerRays[side][finger]); - } - } - }); + Script.scriptEnding.connect(function () { + cleanUp(); }); - - Script.update.connect(function() { - + + Script.update.connect(function () { + + if (!handTouchEnabled) { + return; + } + // index of the finger that needs to be updated this frame - updateFingerWithIndex = (updateFingerWithIndex < fingerKeys.length-1) ? updateFingerWithIndex + 1 : 0; - + ["right", "left"].forEach(function(side) { - + if (!palmData[side].set) { reEstimatePalmData(); recreateRayPicks(); } - + // recalculate the base data updateSphereHand(side); activateNextRay(side, updateFingerWithIndex); - + // this vars manage the transition to default pose var isHandTouching = getTouching(side); countToDefault[side] = isHandTouching ? 0 : countToDefault[side] + 1; - - + for (var i = 0; i < fingerKeys.length; i++) { var finger = fingerKeys[i]; var jointSuffixes = 3; // We need to update rotation of the 0, 1 and 2 joints - var names = getJointNames(side, finger, jointSuffixes); - + var names = getJointNames(side, finger, jointSuffixes); + // Add the animation increments - - dataCurrent[side][finger] = addVals(dataCurrent[side][finger], dataDelta[side][finger], 1); - + dataCurrent[side][finger] = addVals(dataCurrent[side][finger], dataDelta[side][finger], 1); + // update every finger joint - for (var j = 0; j < names.length; j++) { var index = MyAvatar.getJointIndex(names[j]); // if no finger is touching restate the default poses - if (isHandTouching || (dataDefault[side].set && + if (isHandTouching || (dataDefault[side].set && countToDefault[side] < fingerKeys.length*touchAnimationSteps)) { var quatRot = dataCurrent[side][finger][j]; - MyAvatar.setJointRotation(index, quatRot); + MyAvatar.setJointRotation(index, quatRot); } else { MyAvatar.clearJointData(index); } diff --git a/scripts/system/edit.js b/scripts/system/edit.js index d99734f7a4..0789e1fac2 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -63,6 +63,15 @@ var createToolsWindow = new CreateWindow( false ); +/** + * @description Returns true in case we should use the tablet version of the CreateApp + * @returns boolean + */ +var shouldUseEditTabletApp = function() { + return HMD.active || (!HMD.active && !Settings.getValue("desktopTabletBecomesToolbar", true)); +}; + + var selectionDisplay = SelectionDisplay; var selectionManager = SelectionManager; @@ -88,11 +97,12 @@ var cameraManager = new CameraManager(); var grid = new Grid(); var gridTool = new GridTool({ horizontalGrid: grid, - createToolsWindow: createToolsWindow + createToolsWindow: createToolsWindow, + shouldUseEditTabletApp: shouldUseEditTabletApp }); gridTool.setVisible(false); -var entityListTool = new EntityListTool(); +var entityListTool = new EntityListTool(shouldUseEditTabletApp); selectionManager.addEventListener(function () { selectionDisplay.updateHandles(); @@ -578,7 +588,8 @@ var toolBar = (function () { }); createButton = activeButton; tablet.screenChanged.connect(function (type, url) { - var isGoingToHomescreenOnDesktop = (!HMD.active && (url === 'hifi/tablet/TabletHome.qml' || url === '')); + var isGoingToHomescreenOnDesktop = (!shouldUseEditTabletApp() && + (url === 'hifi/tablet/TabletHome.qml' || url === '')); if (isActive && (type !== "QML" || url !== "hifi/tablet/Edit.qml") && !isGoingToHomescreenOnDesktop) { that.setActive(false); } @@ -605,7 +616,7 @@ var toolBar = (function () { }); function createNewEntityDialogButtonCallback(entityType) { return function() { - if (HMD.active) { + if (shouldUseEditTabletApp()) { // tablet version of new-model dialog var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); tablet.pushOntoStack("hifi/tablet/New" + entityType + "Dialog.qml"); @@ -837,7 +848,7 @@ var toolBar = (function () { selectionDisplay.triggerMapping.disable(); tablet.landscape = false; } else { - if (HMD.active) { + if (shouldUseEditTabletApp()) { tablet.loadQMLSource("hifi/tablet/Edit.qml", true); } else { // make other apps inactive while in desktop mode @@ -1989,8 +2000,8 @@ var PropertiesTool = function (opts) { that.setVisible = function (newVisible) { visible = newVisible; - webView.setVisible(HMD.active && visible); - createToolsWindow.setVisible(!HMD.active && visible); + webView.setVisible(shouldUseEditTabletApp() && visible); + createToolsWindow.setVisible(!shouldUseEditTabletApp() && visible); }; that.setVisible(false); @@ -2416,7 +2427,7 @@ function selectParticleEntity(entityID) { // Switch to particle explorer var selectTabMethod = { method: 'selectTab', params: { id: 'particle' } }; - if (HMD.active) { + if (shouldUseEditTabletApp()) { var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); tablet.sendToQml(selectTabMethod); } else { diff --git a/scripts/system/libraries/entityList.js b/scripts/system/libraries/entityList.js index ae89b63ea6..fb876302dd 100644 --- a/scripts/system/libraries/entityList.js +++ b/scripts/system/libraries/entityList.js @@ -11,7 +11,7 @@ /* global EntityListTool, Tablet, selectionManager, Entities, Camera, MyAvatar, Vec3, Menu, Messages, cameraManager, MENU_EASE_ON_FOCUS, deleteSelectedEntities, toggleSelectedEntitiesLocked, toggleSelectedEntitiesVisible */ -EntityListTool = function() { +EntityListTool = function(shouldUseEditTabletApp) { var that = {}; var CreateWindow = Script.require('../modules/createWindow.js'); @@ -55,8 +55,8 @@ EntityListTool = function() { that.setVisible = function(newVisible) { visible = newVisible; - webView.setVisible(HMD.active && visible); - entityListWindow.setVisible(!HMD.active && visible); + webView.setVisible(shouldUseEditTabletApp() && visible); + entityListWindow.setVisible(!shouldUseEditTabletApp() && visible); }; that.setVisible(false); diff --git a/scripts/system/libraries/gridTool.js b/scripts/system/libraries/gridTool.js index 669083a545..3a114f23c7 100644 --- a/scripts/system/libraries/gridTool.js +++ b/scripts/system/libraries/gridTool.js @@ -1,6 +1,6 @@ var GRID_CONTROLS_HTML_URL = Script.resolvePath('../html/gridControls.html'); -Grid = function(opts) { +Grid = function() { var that = {}; var gridColor = { red: 0, green: 0, blue: 0 }; var gridAlpha = 0.6; @@ -247,6 +247,7 @@ GridTool = function(opts) { var horizontalGrid = opts.horizontalGrid; var verticalGrid = opts.verticalGrid; var createToolsWindow = opts.createToolsWindow; + var shouldUseEditTabletApp = opts.shouldUseEditTabletApp; var listeners = []; var webView = null; @@ -299,7 +300,7 @@ GridTool = function(opts) { }; that.setVisible = function(visible) { - webView.setVisible(HMD.active && visible); + webView.setVisible(shouldUseEditTabletApp() && visible); }; return that;