From 1e6b2682c96ce8da9fb1c1a2140c8cf21b4711bc Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 21 Apr 2017 10:17:57 -0700 Subject: [PATCH 01/26] use timers rather than relying on update calls --- scripts/system/controllers/handControllerGrab.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 464101a4e3..1091906de7 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -3984,7 +3984,7 @@ var updateTotalWork = 0; var UPDATE_PERFORMANCE_DEBUGGING = false; -function updateWrapper(){ +var updateWrapper = function () { intervalCount++; var thisInterval = Date.now(); @@ -4032,12 +4032,14 @@ function updateWrapper(){ updateTotalWork = 0; } + Script.setTimeout(updateWrapper, 16); } -Script.update.connect(updateWrapper); +// Script.update.connect(updateWrapper); +Script.setTimeout(updateWrapper, 16); function cleanup() { Menu.removeMenuItem("Developer", "Show Grab Sphere"); - Script.update.disconnect(updateWrapper); + // Script.update.disconnect(updateWrapper); rightController.cleanup(); leftController.cleanup(); Controller.disableMapping(MAPPING_NAME); From ec687bdb4b90ea6ec673dc212581618b3e9a7cf6 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 21 Apr 2017 11:03:43 -0700 Subject: [PATCH 02/26] cleanups --- scripts/system/controllers/handControllerGrab.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 1091906de7..a724f19be7 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -31,6 +31,8 @@ var WANT_DEBUG = false; var WANT_DEBUG_STATE = false; var WANT_DEBUG_SEARCH_NAME = null; +var UPDATE_SLEEP_MS = 16; // how many milliseconds to wait between "update" calls + var FORCE_IGNORE_IK = false; var SHOW_GRAB_POINT_SPHERE = false; @@ -4032,14 +4034,12 @@ var updateWrapper = function () { updateTotalWork = 0; } - Script.setTimeout(updateWrapper, 16); + Script.setTimeout(updateWrapper, UPDATE_SLEEP_MS); } -// Script.update.connect(updateWrapper); -Script.setTimeout(updateWrapper, 16); +Script.setTimeout(updateWrapper, UPDATE_SLEEP_MS); function cleanup() { Menu.removeMenuItem("Developer", "Show Grab Sphere"); - // Script.update.disconnect(updateWrapper); rightController.cleanup(); leftController.cleanup(); Controller.disableMapping(MAPPING_NAME); From 37233b9fdb8e2a2d68a3cba1d6afd95d8857f85c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 26 Apr 2017 10:19:50 -0700 Subject: [PATCH 03/26] avoid a rare script-crash --- scripts/system/controllers/grab.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/system/controllers/grab.js b/scripts/system/controllers/grab.js index 10f477b3af..f96ad81719 100644 --- a/scripts/system/controllers/grab.js +++ b/scripts/system/controllers/grab.js @@ -447,6 +447,10 @@ Grabber.prototype.moveEvent = function(event) { // see if something added/restored gravity var entityProperties = Entities.getEntityProperties(this.entityID); + if (!entityProperties || !entityProperties.gravity) { + return; + } + if (Vec3.length(entityProperties.gravity) !== 0.0) { this.originalGravity = entityProperties.gravity; } From 186b86e3d49994ccee9a441c1135bbee050ebc09 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 26 Apr 2017 10:23:20 -0700 Subject: [PATCH 04/26] load stylus model before it's needed so that it will appear quickly --- scripts/system/tablet-ui/tabletUI.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index dbfd3e632e..cacd9663ab 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -13,7 +13,7 @@ // /* global Script, HMD, WebTablet, UIWebTablet, UserActivityLogger, Settings, Entities, Messages, Tablet, Overlays, - MyAvatar, Menu */ + MyAvatar, Menu, Vec3 */ (function() { // BEGIN LOCAL_SCOPE var tabletRezzed = false; @@ -214,6 +214,23 @@ closeTabletUI(); rezTablet(); tabletShown = false; + + // also cause the stylus model to be loaded + var tmpStylusID = Overlays.addOverlay("model", { + name: "stylus", + url: Script.resourcesPath() + "meshes/tablet-stylus-fat.fbx", + position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.1, z: -2})), + dimensions: { x: 0.01, y: 0.01, z: 0.2 }, + solid: true, + visible: true, + ignoreRayIntersection: true, + drawInFront: false, + lifetime: 3 + }); + Script.setTimeout(function() { + Overlays.deleteOverlay(tmpStylusID); + }, 300); + } else if (!tabletShown) { hideTabletUI(); } From 15661987be71f732859a39cdb91a4c4bd55a02f2 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 27 Apr 2017 10:16:08 -0700 Subject: [PATCH 05/26] load tablet overlay models with higher priority --- interface/src/ui/overlays/ModelOverlay.cpp | 11 ++++++++++- interface/src/ui/overlays/ModelOverlay.h | 3 +++ scripts/system/controllers/handControllerGrab.js | 1 + scripts/system/libraries/WebTablet.js | 1 + scripts/system/tablet-ui/tabletUI.js | 1 + 5 files changed, 16 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index ccaa1d4fbc..07fc2a2b9d 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -22,6 +22,7 @@ ModelOverlay::ModelOverlay() _modelTextures(QVariantMap()) { _model->init(); + _model->setLoadingPriority(_loadPriority); _isLoaded = false; } @@ -30,9 +31,11 @@ ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) : _model(std::make_shared(std::make_shared(), nullptr, this)), _modelTextures(QVariantMap()), _url(modelOverlay->_url), - _updateModel(false) + _updateModel(false), + _loadPriority(modelOverlay->getLoadPriority()) { _model->init(); + _model->setLoadingPriority(_loadPriority); if (_url.isValid()) { _updateModel = true; _isLoaded = false; @@ -113,6 +116,12 @@ void ModelOverlay::setProperties(const QVariantMap& properties) { _updateModel = true; } + auto loadPriorityProperty = properties["loadPriority"]; + if (loadPriorityProperty.isValid()) { + _loadPriority = loadPriorityProperty.toFloat(); + _model->setLoadingPriority(_loadPriority); + } + auto urlValue = properties["url"]; if (urlValue.isValid() && urlValue.canConvert()) { _url = urlValue.toString(); diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index a3ddeed480..e389181879 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -41,6 +41,8 @@ public: void locationChanged(bool tellPhysics) override; + float getLoadPriority() const { return _loadPriority; } + private: ModelPointer _model; @@ -49,6 +51,7 @@ private: QUrl _url; bool _updateModel = { false }; bool _scaleToFit = { false }; + float _loadPriority { 0.0f }; }; #endif // hifi_ModelOverlay_h diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 8a1ee8dff4..992c20ef72 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1336,6 +1336,7 @@ function MyController(hand) { var stylusProperties = { name: "stylus", url: Script.resourcesPath() + "meshes/tablet-stylus-fat.fbx", + loadPriority: 10.0, localPosition: Vec3.sum({ x: 0.0, y: WEB_TOUCH_Y_OFFSET, z: 0.0 }, diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index e71aefc51e..6aa3c4de8a 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -120,6 +120,7 @@ WebTablet = function (url, width, dpi, hand, clientOnly, location) { modelURL: modelURL, url: modelURL, // for overlay grabbable: true, // for overlay + loadPriority: 10.0, // for overlay userData: JSON.stringify({ "grabbableKey": {"grabbable": true} }), diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index cacd9663ab..8a7d0a2ec4 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -219,6 +219,7 @@ var tmpStylusID = Overlays.addOverlay("model", { name: "stylus", url: Script.resourcesPath() + "meshes/tablet-stylus-fat.fbx", + loadPriority: 10.0, position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, {x: 0, y: 0.1, z: -2})), dimensions: { x: 0.01, y: 0.01, z: 0.2 }, solid: true, From 074a11306c02e850774a237f588d75296f63e099 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 1 May 2017 13:21:52 -0700 Subject: [PATCH 06/26] Add support for atp and file urls in OBJReader --- libraries/fbx/src/OBJReader.cpp | 314 +++++++++--------- libraries/fbx/src/OBJReader.h | 1 - .../networking/src/AssetResourceRequest.cpp | 6 +- .../networking/src/AssetResourceRequest.h | 2 +- libraries/networking/src/ResourceManager.cpp | 2 +- 5 files changed, 167 insertions(+), 158 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 7b46556530..167cb8caac 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -24,6 +24,7 @@ #include #include +#include #include "FBXReader.h" #include "ModelFormatLogging.h" @@ -165,6 +166,7 @@ bool OBJFace::add(const QByteArray& vertexIndex, const QByteArray& textureIndex, } return true; } + QVector OBJFace::triangulate() { QVector newFaces; const int nVerticesInATriangle = 3; @@ -183,6 +185,7 @@ QVector OBJFace::triangulate() { } return newFaces; } + void OBJFace::addFrom(const OBJFace* face, int index) { // add using data from f at index i vertexIndices.append(face->vertexIndices[index]); if (face->textureUVIndices.count() > 0) { // Any at all. Runtime error if not consistent. @@ -193,24 +196,13 @@ void OBJFace::addFrom(const OBJFace* face, int index) { // add using data from f } } -static bool replyOK(QNetworkReply* netReply, QUrl url) { // This will be reworked when we make things asynchronous - return (netReply && netReply->isFinished() && - (url.toString().startsWith("file", Qt::CaseInsensitive) ? // file urls don't have http status codes - netReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString().isEmpty() : - (netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200))); -} - bool OBJReader::isValidTexture(const QByteArray &filename) { if (_url.isEmpty()) { return false; } QUrl candidateUrl = _url.resolved(QUrl(filename)); - QNetworkReply *netReply = request(candidateUrl, true); - bool isValid = replyOK(netReply, candidateUrl); - if (netReply) { - netReply->deleteLater(); - } - return isValid; + + return ResourceManager::resourceExists(candidateUrl); } void OBJReader::parseMaterialLibrary(QIODevice* device) { @@ -274,7 +266,28 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) { } } -QNetworkReply* OBJReader::request(QUrl& url, bool isTest) { +std::tuple requestData(QUrl& url) { + auto request = ResourceManager::createResourceRequest(nullptr, url); + + if (!request) { + return std::make_tuple(false, QByteArray()); + } + + request->send(); + + QEventLoop loop; + QObject::connect(request, &ResourceRequest::finished, &loop, &QEventLoop::quit); + loop.exec(); + + if (request->getResult() == ResourceRequest::Success) { + return std::make_tuple(true, request->getData()); + } else { + return std::make_tuple(false, QByteArray()); + } +} + + +QNetworkReply* request(QUrl& url, bool isTest) { if (!qApp) { return nullptr; } @@ -293,10 +306,7 @@ QNetworkReply* OBJReader::request(QUrl& url, bool isTest) { QEventLoop loop; // Create an event loop that will quit when we get the finished signal QObject::connect(netReply, SIGNAL(finished()), &loop, SLOT(quit())); loop.exec(); // Nothing is going to happen on this whole run thread until we get this - static const int WAIT_TIMEOUT_MS = 500; - while (!aboutToQuit && qApp && !netReply->isReadable()) { - netReply->waitForReadyRead(WAIT_TIMEOUT_MS); // so we might as well block this thread waiting for the response, rather than - } + QObject::disconnect(connection); return netReply; // trying to sync later on. } @@ -446,142 +456,142 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, // add a new meshPart to the geometry's single mesh. while (parseOBJGroup(tokenizer, mapping, geometry, scaleGuess, combineParts)) {} - FBXMesh& mesh = geometry.meshes[0]; - mesh.meshIndex = 0; + FBXMesh& mesh = geometry.meshes[0]; + mesh.meshIndex = 0; - geometry.joints.resize(1); - geometry.joints[0].isFree = false; - geometry.joints[0].parentIndex = -1; - geometry.joints[0].distanceToParent = 0; - geometry.joints[0].translation = glm::vec3(0, 0, 0); - geometry.joints[0].rotationMin = glm::vec3(0, 0, 0); - geometry.joints[0].rotationMax = glm::vec3(0, 0, 0); - geometry.joints[0].name = "OBJ"; - geometry.joints[0].isSkeletonJoint = true; + geometry.joints.resize(1); + geometry.joints[0].isFree = false; + geometry.joints[0].parentIndex = -1; + geometry.joints[0].distanceToParent = 0; + geometry.joints[0].translation = glm::vec3(0, 0, 0); + geometry.joints[0].rotationMin = glm::vec3(0, 0, 0); + geometry.joints[0].rotationMax = glm::vec3(0, 0, 0); + geometry.joints[0].name = "OBJ"; + geometry.joints[0].isSkeletonJoint = true; - geometry.jointIndices["x"] = 1; + geometry.jointIndices["x"] = 1; - FBXCluster cluster; - cluster.jointIndex = 0; - cluster.inverseBindMatrix = glm::mat4(1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1); - mesh.clusters.append(cluster); + FBXCluster cluster; + cluster.jointIndex = 0; + cluster.inverseBindMatrix = glm::mat4(1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + mesh.clusters.append(cluster); - QMap materialMeshIdMap; - QVector fbxMeshParts; - for (int i = 0, meshPartCount = 0; i < mesh.parts.count(); i++, meshPartCount++) { - FBXMeshPart& meshPart = mesh.parts[i]; - FaceGroup faceGroup = faceGroups[meshPartCount]; + QMap materialMeshIdMap; + QVector fbxMeshParts; + for (int i = 0, meshPartCount = 0; i < mesh.parts.count(); i++, meshPartCount++) { + FBXMeshPart& meshPart = mesh.parts[i]; + FaceGroup faceGroup = faceGroups[meshPartCount]; bool specifiesUV = false; - foreach(OBJFace face, faceGroup) { - // Go through all of the OBJ faces and determine the number of different materials necessary (each different material will be a unique mesh). - // NOTE (trent/mittens 3/30/17): this seems hardcore wasteful and is slowed down a bit by iterating through the face group twice, but it's the best way I've thought of to hack multi-material support in an OBJ into this pipeline. - if (!materialMeshIdMap.contains(face.materialName)) { - // Create a new FBXMesh for this material mapping. - materialMeshIdMap.insert(face.materialName, materialMeshIdMap.count()); + foreach(OBJFace face, faceGroup) { + // Go through all of the OBJ faces and determine the number of different materials necessary (each different material will be a unique mesh). + // NOTE (trent/mittens 3/30/17): this seems hardcore wasteful and is slowed down a bit by iterating through the face group twice, but it's the best way I've thought of to hack multi-material support in an OBJ into this pipeline. + if (!materialMeshIdMap.contains(face.materialName)) { + // Create a new FBXMesh for this material mapping. + materialMeshIdMap.insert(face.materialName, materialMeshIdMap.count()); - fbxMeshParts.append(FBXMeshPart()); - FBXMeshPart& meshPartNew = fbxMeshParts.last(); - meshPartNew.quadIndices = QVector(meshPart.quadIndices); // Copy over quad indices [NOTE (trent/mittens, 4/3/17): Likely unnecessary since they go unused anyway]. - meshPartNew.quadTrianglesIndices = QVector(meshPart.quadTrianglesIndices); // Copy over quad triangulated indices [NOTE (trent/mittens, 4/3/17): Likely unnecessary since they go unused anyway]. - meshPartNew.triangleIndices = QVector(meshPart.triangleIndices); // Copy over triangle indices. + fbxMeshParts.append(FBXMeshPart()); + FBXMeshPart& meshPartNew = fbxMeshParts.last(); + meshPartNew.quadIndices = QVector(meshPart.quadIndices); // Copy over quad indices [NOTE (trent/mittens, 4/3/17): Likely unnecessary since they go unused anyway]. + meshPartNew.quadTrianglesIndices = QVector(meshPart.quadTrianglesIndices); // Copy over quad triangulated indices [NOTE (trent/mittens, 4/3/17): Likely unnecessary since they go unused anyway]. + meshPartNew.triangleIndices = QVector(meshPart.triangleIndices); // Copy over triangle indices. - // Do some of the material logic (which previously lived below) now. - // All the faces in the same group will have the same name and material. - QString groupMaterialName = face.materialName; - if (groupMaterialName.isEmpty() && specifiesUV) { + // Do some of the material logic (which previously lived below) now. + // All the faces in the same group will have the same name and material. + QString groupMaterialName = face.materialName; + if (groupMaterialName.isEmpty() && specifiesUV) { #ifdef WANT_DEBUG - qCDebug(modelformat) << "OBJ Reader WARNING: " << url - << " needs a texture that isn't specified. Using default mechanism."; + qCDebug(modelformat) << "OBJ Reader WARNING: " << url + << " needs a texture that isn't specified. Using default mechanism."; #endif - groupMaterialName = SMART_DEFAULT_MATERIAL_NAME; - } - if (!groupMaterialName.isEmpty()) { - OBJMaterial& material = materials[groupMaterialName]; - if (specifiesUV) { - material.userSpecifiesUV = true; // Note might not be true in a later usage. - } - if (specifiesUV || (groupMaterialName.compare("none", Qt::CaseInsensitive) != 0)) { - // Blender has a convention that a material named "None" isn't really used (or defined). - material.used = true; - needsMaterialLibrary = groupMaterialName != SMART_DEFAULT_MATERIAL_NAME; - } - materials[groupMaterialName] = material; - meshPartNew.materialID = groupMaterialName; - } - } - } - } - - // clean up old mesh parts. - int unmodifiedMeshPartCount = mesh.parts.count(); - mesh.parts.clear(); - mesh.parts = QVector(fbxMeshParts); - - for (int i = 0, meshPartCount = 0; i < unmodifiedMeshPartCount; i++, meshPartCount++) { - FaceGroup faceGroup = faceGroups[meshPartCount]; - - // Now that each mesh has been created with its own unique material mappings, fill them with data (vertex data is duplicated, face data is not). - foreach(OBJFace face, faceGroup) { - FBXMeshPart& meshPart = mesh.parts[materialMeshIdMap[face.materialName]]; - - glm::vec3 v0 = checked_at(vertices, face.vertexIndices[0]); - glm::vec3 v1 = checked_at(vertices, face.vertexIndices[1]); - glm::vec3 v2 = checked_at(vertices, face.vertexIndices[2]); - - // Scale the vertices if the OBJ file scale is specified as non-one. - if (scaleGuess != 1.0f) { - v0 *= scaleGuess; - v1 *= scaleGuess; - v2 *= scaleGuess; - } - - // Add the vertices. - meshPart.triangleIndices.append(mesh.vertices.count()); // not face.vertexIndices into vertices - mesh.vertices << v0; - meshPart.triangleIndices.append(mesh.vertices.count()); - mesh.vertices << v1; - meshPart.triangleIndices.append(mesh.vertices.count()); - mesh.vertices << v2; - - glm::vec3 n0, n1, n2; - if (face.normalIndices.count()) { - n0 = checked_at(normals, face.normalIndices[0]); - n1 = checked_at(normals, face.normalIndices[1]); - n2 = checked_at(normals, face.normalIndices[2]); - } else { - // generate normals from triangle plane if not provided - n0 = n1 = n2 = glm::cross(v1 - v0, v2 - v0); - } - - mesh.normals.append(n0); - mesh.normals.append(n1); - mesh.normals.append(n2); - - if (face.textureUVIndices.count()) { - mesh.texCoords << - checked_at(textureUVs, face.textureUVIndices[0]) << - checked_at(textureUVs, face.textureUVIndices[1]) << - checked_at(textureUVs, face.textureUVIndices[2]); - } else { - glm::vec2 corner(0.0f, 1.0f); - mesh.texCoords << corner << corner << corner; - } - } + groupMaterialName = SMART_DEFAULT_MATERIAL_NAME; + } + if (!groupMaterialName.isEmpty()) { + OBJMaterial& material = materials[groupMaterialName]; + if (specifiesUV) { + material.userSpecifiesUV = true; // Note might not be true in a later usage. + } + if (specifiesUV || (groupMaterialName.compare("none", Qt::CaseInsensitive) != 0)) { + // Blender has a convention that a material named "None" isn't really used (or defined). + material.used = true; + needsMaterialLibrary = groupMaterialName != SMART_DEFAULT_MATERIAL_NAME; + } + materials[groupMaterialName] = material; + meshPartNew.materialID = groupMaterialName; + } + } + } } - mesh.meshExtents.reset(); - foreach(const glm::vec3& vertex, mesh.vertices) { - mesh.meshExtents.addPoint(vertex); - geometry.meshExtents.addPoint(vertex); - } + // clean up old mesh parts. + int unmodifiedMeshPartCount = mesh.parts.count(); + mesh.parts.clear(); + mesh.parts = QVector(fbxMeshParts); - // Build the single mesh. - FBXReader::buildModelMesh(mesh, url.toString()); + for (int i = 0, meshPartCount = 0; i < unmodifiedMeshPartCount; i++, meshPartCount++) { + FaceGroup faceGroup = faceGroups[meshPartCount]; - // fbxDebugDump(geometry); + // Now that each mesh has been created with its own unique material mappings, fill them with data (vertex data is duplicated, face data is not). + foreach(OBJFace face, faceGroup) { + FBXMeshPart& meshPart = mesh.parts[materialMeshIdMap[face.materialName]]; + + glm::vec3 v0 = checked_at(vertices, face.vertexIndices[0]); + glm::vec3 v1 = checked_at(vertices, face.vertexIndices[1]); + glm::vec3 v2 = checked_at(vertices, face.vertexIndices[2]); + + // Scale the vertices if the OBJ file scale is specified as non-one. + if (scaleGuess != 1.0f) { + v0 *= scaleGuess; + v1 *= scaleGuess; + v2 *= scaleGuess; + } + + // Add the vertices. + meshPart.triangleIndices.append(mesh.vertices.count()); // not face.vertexIndices into vertices + mesh.vertices << v0; + meshPart.triangleIndices.append(mesh.vertices.count()); + mesh.vertices << v1; + meshPart.triangleIndices.append(mesh.vertices.count()); + mesh.vertices << v2; + + glm::vec3 n0, n1, n2; + if (face.normalIndices.count()) { + n0 = checked_at(normals, face.normalIndices[0]); + n1 = checked_at(normals, face.normalIndices[1]); + n2 = checked_at(normals, face.normalIndices[2]); + } else { + // generate normals from triangle plane if not provided + n0 = n1 = n2 = glm::cross(v1 - v0, v2 - v0); + } + + mesh.normals.append(n0); + mesh.normals.append(n1); + mesh.normals.append(n2); + + if (face.textureUVIndices.count()) { + mesh.texCoords << + checked_at(textureUVs, face.textureUVIndices[0]) << + checked_at(textureUVs, face.textureUVIndices[1]) << + checked_at(textureUVs, face.textureUVIndices[2]); + } else { + glm::vec2 corner(0.0f, 1.0f); + mesh.texCoords << corner << corner << corner; + } + } + } + + mesh.meshExtents.reset(); + foreach(const glm::vec3& vertex, mesh.vertices) { + mesh.meshExtents.addPoint(vertex); + geometry.meshExtents.addPoint(vertex); + } + + // Build the single mesh. + FBXReader::buildModelMesh(mesh, url.toString()); + + // fbxDebugDump(geometry); } catch(const std::exception& e) { qCDebug(modelformat) << "OBJ reader fail: " << e.what(); } @@ -624,15 +634,15 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, // Throw away any path part of libraryName, and merge against original url. QUrl libraryUrl = _url.resolved(QUrl(libraryName).fileName()); qCDebug(modelformat) << "OBJ Reader material library" << libraryName << "used in" << _url; - QNetworkReply* netReply = request(libraryUrl, false); - if (replyOK(netReply, libraryUrl)) { - parseMaterialLibrary(netReply); + bool success; + QByteArray data; + std::tie(success, data) = requestData(libraryUrl); + if (success) { + QBuffer buffer { &data }; + buffer.open(QIODevice::ReadOnly); + parseMaterialLibrary(&buffer); } else { - qCDebug(modelformat) << "OBJ Reader WARNING:" << libraryName << "did not answer. Got" - << (!netReply ? "aborted" : netReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString()); - } - if (netReply) { - netReply->deleteLater(); + qCDebug(modelformat) << "OBJ Reader WARNING:" << libraryName << "did not answer"; } } } @@ -655,9 +665,9 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, if (!objMaterial.diffuseTextureFilename.isEmpty()) { fbxMaterial.albedoTexture.filename = objMaterial.diffuseTextureFilename; } - if (!objMaterial.specularTextureFilename.isEmpty()) { - fbxMaterial.specularTexture.filename = objMaterial.specularTextureFilename; - } + if (!objMaterial.specularTextureFilename.isEmpty()) { + fbxMaterial.specularTexture.filename = objMaterial.specularTextureFilename; + } modelMaterial->setEmissive(fbxMaterial.emissiveColor); modelMaterial->setAlbedo(fbxMaterial.diffuseColor); diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 4be5705f9a..18a4b89f1e 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -72,7 +72,6 @@ public: QString currentMaterialName; QHash materials; - QNetworkReply* request(QUrl& url, bool isTest); FBXGeometry* readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url = QUrl()); private: diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index 092e0ccb3d..a4d5d66923 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -40,16 +40,16 @@ AssetResourceRequest::~AssetResourceRequest() { } } -bool AssetResourceRequest::urlIsAssetHash() const { +bool AssetResourceRequest::urlIsAssetHash(const QUrl& url) { static const QString ATP_HASH_REGEX_STRING { "^atp:([A-Fa-f0-9]{64})(\\.[\\w]+)?$" }; QRegExp hashRegex { ATP_HASH_REGEX_STRING }; - return hashRegex.exactMatch(_url.toString()); + return hashRegex.exactMatch(url.toString()); } void AssetResourceRequest::doSend() { // We'll either have a hash or an ATP path to a file (that maps to a hash) - if (urlIsAssetHash()) { + if (urlIsAssetHash(_url)) { // We've detected that this is a hash - simply use AssetClient to request that asset auto parts = _url.path().split(".", QString::SkipEmptyParts); auto hash = parts.length() > 0 ? parts[0] : ""; diff --git a/libraries/networking/src/AssetResourceRequest.h b/libraries/networking/src/AssetResourceRequest.h index 3f110fae17..18b82f2573 100644 --- a/libraries/networking/src/AssetResourceRequest.h +++ b/libraries/networking/src/AssetResourceRequest.h @@ -32,7 +32,7 @@ private slots: void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal); private: - bool urlIsAssetHash() const; + static bool urlIsAssetHash(const QUrl& url); void requestMappingForPath(const AssetPath& path); void requestHash(const AssetHash& hash); diff --git a/libraries/networking/src/ResourceManager.cpp b/libraries/networking/src/ResourceManager.cpp index 439d44c940..6492e9171e 100644 --- a/libraries/networking/src/ResourceManager.cpp +++ b/libraries/networking/src/ResourceManager.cpp @@ -14,10 +14,10 @@ #include #include #include +#include #include - #include "AssetResourceRequest.h" #include "FileResourceRequest.h" #include "HTTPResourceRequest.h" From c839118c6be5de2ac17366a97f42175a83e91e7a Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 1 May 2017 13:22:23 -0700 Subject: [PATCH 07/26] Add ResourceManager::resourceExists --- libraries/networking/src/ResourceManager.cpp | 48 ++++++++++++++++++++ libraries/networking/src/ResourceManager.h | 4 ++ 2 files changed, 52 insertions(+) diff --git a/libraries/networking/src/ResourceManager.cpp b/libraries/networking/src/ResourceManager.cpp index 6492e9171e..e2c1cf2431 100644 --- a/libraries/networking/src/ResourceManager.cpp +++ b/libraries/networking/src/ResourceManager.cpp @@ -116,3 +116,51 @@ ResourceRequest* ResourceManager::createResourceRequest(QObject* parent, const Q request->moveToThread(&_thread); return request; } + + +bool ResourceManager::resourceExists(const QUrl& url) { + auto scheme = url.scheme(); + if (scheme == URL_SCHEME_FILE) { + QFileInfo file { url.toString() }; + return file.exists(); + } else if (scheme == URL_SCHEME_HTTP || scheme == URL_SCHEME_HTTPS || scheme == URL_SCHEME_FTP) { + auto& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkRequest request { url }; + + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + + auto reply = networkAccessManager.head(request); + + QEventLoop loop; + QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + loop.exec(); + + reply->deleteLater(); + + return reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200; + } else if (scheme == URL_SCHEME_ATP) { + auto request = new AssetResourceRequest(url); + ByteRange range; + range.fromInclusive = 1; + range.toExclusive = 1; + request->setByteRange(range); + request->setCacheEnabled(false); + + QEventLoop loop; + + QObject::connect(request, &AssetResourceRequest::finished, &loop, &QEventLoop::quit); + + request->send(); + + loop.exec(); + + request->deleteLater(); + + return request->getResult() == ResourceRequest::Success; + } + + qCDebug(networking) << "Unknown scheme (" << scheme << ") for URL: " << url.url(); + return false; +} + diff --git a/libraries/networking/src/ResourceManager.h b/libraries/networking/src/ResourceManager.h index d193c39cae..41da892701 100644 --- a/libraries/networking/src/ResourceManager.h +++ b/libraries/networking/src/ResourceManager.h @@ -36,6 +36,10 @@ public: static void init(); static void cleanup(); + // Blocking call to check if a resource exists. This function uses a QEventLoop internally + // to return to the calling thread so that events can still be processed. + static bool resourceExists(const QUrl& url); + private: static QThread _thread; From 9b117165bd21d7a3ff21c47fa5190ab4dcd2590d Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 2 May 2017 11:44:16 -0700 Subject: [PATCH 08/26] expire our announcements when we leave, and turn off messagesWaiting when everything is expired. --- scripts/system/tablet-goto.js | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/scripts/system/tablet-goto.js b/scripts/system/tablet-goto.js index fec7a6de90..b9ca6f7407 100644 --- a/scripts/system/tablet-goto.js +++ b/scripts/system/tablet-goto.js @@ -143,9 +143,22 @@ button.clicked.connect(onClicked); tablet.screenChanged.connect(onScreenChanged); - var stories = {}; - var DEBUG = false; + var stories = {}, pingPong = false; + var DEBUG = true; //fixme + function expire(id) { + request({ + uri: location.metaverseServerUrl + '/api/v1/user_stories/' + id, + method: 'PUT', + body: {expired: true} + }, function (error, response) { + if (error || (response.status !== 'success')) { + print("ERROR expiring story: ", error || response.status); + return; + } + }); + } function pollForAnnouncements() { + // We could bail now if !Account.isLoggedIn(), but what if we someday have system-wide announcments? var actions = DEBUG ? 'snapshot' : 'announcement'; var count = DEBUG ? 10 : 100; var options = [ @@ -164,9 +177,16 @@ print("Error: unable to get", url, error || data.status); return; } - var didNotify = false; + var didNotify = false, key; + pingPong = !pingPong; data.user_stories.forEach(function (story) { - if (stories[story.id]) { // already seen + var stored = stories[story.id], storedOrNew = stored || story; + if ((storedOrNew.username === Account.username) && (storyOrNew.place_name !== location.placename)) { + expire(story.id); + return; // before marking + } + storedOrNew.pingPong = pingPong; + if (stored) { // already seen return; } stories[story.id] = story; @@ -174,12 +194,19 @@ Window.displayAnnouncement(message); didNotify = true; }); + for (key in stories) { // Any story we were tracking that was not marked, has expired. + if (stories[key].pingPong !== pingPong) { + delete stories[key]; + } + } if (didNotify) { messagesWaiting(true); if (HMD.isHandControllerAvailable()) { var STRENGTH = 1.0, DURATION_MS = 60, HAND = 2; // both hands Controller.triggerHapticPulse(STRENGTH, DURATION_MS, HAND); } + } else if (!Object.keys(stories).length) { // If there's nothing being tracked, then any messageWaiting has expired. + messagesWaiting(false); } }); } From 84305c20498fe5ceda8fea22b214770c3b5f83cc Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 2 May 2017 17:30:31 -0700 Subject: [PATCH 09/26] Fixing the by region update of the compressed texture to match the 4 x 4 tiles alignment --- libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 8 ++++++-- .../gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp | 6 ++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 5db924dd5c..9fe6bbb772 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -429,9 +429,13 @@ void GL41VariableAllocationTexture::populateTransferQueue() { // consuming more than X bandwidth auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face); const auto lines = mipDimensions.y; - auto bytesPerLine = mipSize / lines; + const uint32_t CHUNK_NUM_LINES { 4 }; + const auto numChunks = (lines + (CHUNK_NUM_LINES - 1)) / CHUNK_NUM_LINES; + auto bytesPerChunk = mipSize / numChunks; + //auto bytesPerLine = mipSize / lines; Q_ASSERT(0 == (mipSize % lines)); - uint32_t linesPerTransfer = (uint32_t)(MAX_TRANSFER_SIZE / bytesPerLine); + // uint32_t linesPerTransfer = (uint32_t)(MAX_TRANSFER_SIZE / bytesPerLine); + uint32_t linesPerTransfer = CHUNK_NUM_LINES * (uint32_t)(MAX_TRANSFER_SIZE / bytesPerChunk); uint32_t lineOffset = 0; while (lineOffset < lines) { uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 92d820e5f0..192b7f3088 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -197,9 +197,11 @@ void GL45ResourceTexture::populateTransferQueue() { // consuming more than X bandwidth auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face); const auto lines = mipDimensions.y; - auto bytesPerLine = mipSize / lines; + const uint32_t CHUNK_NUM_LINES { 4 }; + const auto numChunks = (lines + (CHUNK_NUM_LINES - 1)) / CHUNK_NUM_LINES; + auto bytesPerChunk = mipSize / numChunks; Q_ASSERT(0 == (mipSize % lines)); - uint32_t linesPerTransfer = (uint32_t)(MAX_TRANSFER_SIZE / bytesPerLine); + uint32_t linesPerTransfer = CHUNK_NUM_LINES * (uint32_t)(MAX_TRANSFER_SIZE / bytesPerChunk); uint32_t lineOffset = 0; while (lineOffset < lines) { uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); From c583ffbac447d865914442817eaa5323b1fc67fd Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 3 May 2017 11:30:09 -0700 Subject: [PATCH 10/26] Clean up names and comments --- libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp | 11 +++++------ .../src/gpu/gl45/GL45BackendVariableTexture.cpp | 9 +++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index 9fe6bbb772..a9d1ddb914 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -427,15 +427,14 @@ void GL41VariableAllocationTexture::populateTransferQueue() { // break down the transfers into chunks so that no single transfer is // consuming more than X bandwidth + // For compressed format, regions must be a multiple of the 4x4 tiles, so enforce 4 lines as the minimum block auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face); const auto lines = mipDimensions.y; - const uint32_t CHUNK_NUM_LINES { 4 }; - const auto numChunks = (lines + (CHUNK_NUM_LINES - 1)) / CHUNK_NUM_LINES; - auto bytesPerChunk = mipSize / numChunks; - //auto bytesPerLine = mipSize / lines; + const uint32_t BLOCK_NUM_LINES { 4 }; + const auto numBlocks = (lines + (BLOCK_NUM_LINES - 1)) / BLOCK_NUM_LINES; + auto bytesPerBlock = mipSize / numBlocks; Q_ASSERT(0 == (mipSize % lines)); - // uint32_t linesPerTransfer = (uint32_t)(MAX_TRANSFER_SIZE / bytesPerLine); - uint32_t linesPerTransfer = CHUNK_NUM_LINES * (uint32_t)(MAX_TRANSFER_SIZE / bytesPerChunk); + uint32_t linesPerTransfer = BLOCK_NUM_LINES * (uint32_t)(MAX_TRANSFER_SIZE / bytesPerBlock); uint32_t lineOffset = 0; while (lineOffset < lines) { uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index 192b7f3088..d46f38d88f 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -195,13 +195,14 @@ void GL45ResourceTexture::populateTransferQueue() { // break down the transfers into chunks so that no single transfer is // consuming more than X bandwidth + // For compressed format, regions must be a multiple of the 4x4 tiles, so enforce 4 lines as the minimum block auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face); const auto lines = mipDimensions.y; - const uint32_t CHUNK_NUM_LINES { 4 }; - const auto numChunks = (lines + (CHUNK_NUM_LINES - 1)) / CHUNK_NUM_LINES; - auto bytesPerChunk = mipSize / numChunks; + const uint32_t BLOCK_NUM_LINES { 4 }; + const auto numBlocks = (lines + (BLOCK_NUM_LINES - 1)) / BLOCK_NUM_LINES; + auto bytesPerBlock = mipSize / numBlocks; Q_ASSERT(0 == (mipSize % lines)); - uint32_t linesPerTransfer = CHUNK_NUM_LINES * (uint32_t)(MAX_TRANSFER_SIZE / bytesPerChunk); + uint32_t linesPerTransfer = BLOCK_NUM_LINES * (uint32_t)(MAX_TRANSFER_SIZE / bytesPerBlock); uint32_t lineOffset = 0; while (lineOffset < lines) { uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); From 42887d41c464846877265298600731dbc8538ad7 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 3 May 2017 13:45:48 -0700 Subject: [PATCH 11/26] debugging --- scripts/system/tablet-goto.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts/system/tablet-goto.js b/scripts/system/tablet-goto.js index b9ca6f7407..6c18d0a681 100644 --- a/scripts/system/tablet-goto.js +++ b/scripts/system/tablet-goto.js @@ -145,15 +145,22 @@ var stories = {}, pingPong = false; var DEBUG = true; //fixme + function debug() { + if (!DEBUG) { + return; + } + print([].map.call(arguments, JSON.stringify)); + } + function expire(id) { request({ uri: location.metaverseServerUrl + '/api/v1/user_stories/' + id, method: 'PUT', body: {expired: true} }, function (error, response) { + debug('expired story', id, 'error:', error, 'response:', response); if (error || (response.status !== 'success')) { print("ERROR expiring story: ", error || response.status); - return; } }); } @@ -170,9 +177,11 @@ 'per_page=' + count ]; var url = location.metaverseServerUrl + '/api/v1/user_stories?' + options.join('&'); + url = 'https://highfidelity.com/api/v1/user_stories?include_actions=announcement'; //fixme remove request({ uri: url }, function (error, data) { + debug(url, error, data); if (error || (data.status !== 'success')) { print("Error: unable to get", url, error || data.status); return; @@ -181,6 +190,7 @@ pingPong = !pingPong; data.user_stories.forEach(function (story) { var stored = stories[story.id], storedOrNew = stored || story; + debug('story exists:', !!stored, storedOrNew); if ((storedOrNew.username === Account.username) && (storyOrNew.place_name !== location.placename)) { expire(story.id); return; // before marking From aa74dda9f36bb77a83b9e3f460291d246a91f3ef Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 3 May 2017 16:29:37 -0700 Subject: [PATCH 12/26] cleanup --- scripts/system/tablet-goto.js | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/scripts/system/tablet-goto.js b/scripts/system/tablet-goto.js index 13c4830d31..2cdfecede4 100644 --- a/scripts/system/tablet-goto.js +++ b/scripts/system/tablet-goto.js @@ -16,6 +16,13 @@ (function () { // BEGIN LOCAL_SCOPE var request = Script.require('request').request; + var DEBUG = false; + function debug() { + if (!DEBUG) { + return; + } + print([].map.call(arguments, JSON.stringify)); + } var gotoQmlSource = "TabletAddressDialog.qml"; var buttonName = "GOTO"; @@ -101,14 +108,6 @@ tablet.screenChanged.connect(onScreenChanged); var stories = {}, pingPong = false; - var DEBUG = true; //fixme - function debug() { - if (!DEBUG) { - return; - } - print([].map.call(arguments, JSON.stringify)); - } - function expire(id) { request({ uri: location.metaverseServerUrl + '/api/v1/user_stories/' + id, @@ -123,7 +122,7 @@ } function pollForAnnouncements() { // We could bail now if !Account.isLoggedIn(), but what if we someday have system-wide announcments? - var actions = DEBUG ? 'snapshot' : 'announcement'; + var actions = 'announcement'; var count = DEBUG ? 10 : 100; var options = [ 'now=' + new Date().toISOString(), @@ -134,7 +133,6 @@ 'per_page=' + count ]; var url = location.metaverseServerUrl + '/api/v1/user_stories?' + options.join('&'); - url = 'https://highfidelity.com/api/v1/user_stories?include_actions=announcement'; //fixme remove request({ uri: url }, function (error, data) { @@ -148,7 +146,7 @@ data.user_stories.forEach(function (story) { var stored = stories[story.id], storedOrNew = stored || story; debug('story exists:', !!stored, storedOrNew); - if ((storedOrNew.username === Account.username) && (storyOrNew.place_name !== location.placename)) { + if ((storedOrNew.username === Account.username) && (storedOrNew.place_name !== location.placename)) { expire(story.id); return; // before marking } From 3e57e804655d5d5043b985c4d16e6ba48ae0f519 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 3 May 2017 16:39:34 -0700 Subject: [PATCH 13/26] use new request module. --- scripts/system/snapshot.js | 49 +------------------------------------- 1 file changed, 1 insertion(+), 48 deletions(-) diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index b93595f0b4..762dad1f7e 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -38,54 +38,7 @@ var METAVERSE_BASE = location.metaverseServerUrl; // It's totally unnecessary to return to C++ to perform many of these requests, such as DELETEing an old story, // POSTING a new one, PUTTING a new audience, or GETTING story data. It's far more efficient to do all of that within JS -function request(options, callback) { // cb(error, responseOfCorrectContentType) of url. A subset of npm request. - var httpRequest = new XMLHttpRequest(), key; - // QT bug: apparently doesn't handle onload. Workaround using readyState. - httpRequest.onreadystatechange = function () { - var READY_STATE_DONE = 4; - var HTTP_OK = 200; - if (httpRequest.readyState >= READY_STATE_DONE) { - var error = (httpRequest.status !== HTTP_OK) && httpRequest.status.toString() + ':' + httpRequest.statusText, - response = !error && httpRequest.responseText, - contentType = !error && httpRequest.getResponseHeader('content-type'); - if (!error && contentType.indexOf('application/json') === 0) { // ignoring charset, etc. - try { - response = JSON.parse(response); - } catch (e) { - error = e; - } - } - callback(error, response); - } - }; - if (typeof options === 'string') { - options = { uri: options }; - } - if (options.url) { - options.uri = options.url; - } - if (!options.method) { - options.method = 'GET'; - } - if (options.body && (options.method === 'GET')) { // add query parameters - var params = [], appender = (-1 === options.uri.search('?')) ? '?' : '&'; - for (key in options.body) { - params.push(key + '=' + options.body[key]); - } - options.uri += appender + params.join('&'); - delete options.body; - } - if (options.json) { - options.headers = options.headers || {}; - options.headers["Content-type"] = "application/json"; - options.body = JSON.stringify(options.body); - } - for (key in options.headers || {}) { - httpRequest.setRequestHeader(key, options.headers[key]); - } - httpRequest.open(options.method, options.uri, true); - httpRequest.send(options.body); -} +var request = Script.require('request').request; function openLoginWindow() { if ((HMD.active && Settings.getValue("hmdTabletBecomesToolbar", false)) From 5e36bebc969fc31e15ea871849527407f9946125 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 3 May 2017 19:23:21 -0700 Subject: [PATCH 14/26] Store irradiance in the KTX files. --- libraries/gpu/src/gpu/Texture_ktx.cpp | 82 +++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 5 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index d2f93c0036..d5710bf84b 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -99,6 +99,61 @@ struct GPUKTXPayload { }; const std::string GPUKTXPayload::KEY { "hifi.gpu" }; + +struct IrradianceKTXPayload { + using Version = uint8; + + static const std::string KEY; + static const Version CURRENT_VERSION{ 0 }; + static const size_t PADDING{ 3 }; + static const size_t SIZE{ sizeof(Version) + sizeof(SphericalHarmonics) + PADDING }; + static_assert(IrradianceKTXPayload::SIZE == 148, "Packing size may differ between platforms"); + static_assert(IrradianceKTXPayload::SIZE % 4 == 0, "IrradianceKTXPayload is not 4 bytes aligned"); + + SphericalHarmonics _irradianceSH; + + Byte* serialize(Byte* data) const { + *(Version*)data = CURRENT_VERSION; + data += sizeof(Version); + + memcpy(data, &_irradianceSH, sizeof(SphericalHarmonics)); + data += sizeof(SphericalHarmonics); + + return data + PADDING; + } + + bool unserialize(const Byte* data, size_t size) { + if (size != SIZE) { + return false; + } + + Version version = *(const Version*)data; + if (version != CURRENT_VERSION) { + return false; + } + data += sizeof(Version); + + memcpy(&_irradianceSH, data, sizeof(SphericalHarmonics)); + data += sizeof(SphericalHarmonics); + + return true; + } + + static bool isIrradianceKTX(const ktx::KeyValue& val) { + return (val._key.compare(KEY) == 0); + } + + static bool findInKeyValues(const ktx::KeyValues& keyValues, IrradianceKTXPayload& payload) { + auto found = std::find_if(keyValues.begin(), keyValues.end(), isIrradianceKTX); + if (found != keyValues.end()) { + auto value = found->_value; + return payload.unserialize(value.data(), value.size()); + } + return false; + } +}; +const std::string IrradianceKTXPayload::KEY{ "hifi.irradianceSH" }; + KtxStorage::KtxStorage(const std::string& filename) : _filename(filename) { { // We are doing a lot of work here just to get descriptor data @@ -304,16 +359,27 @@ ktx::KTXUniquePointer Texture::serialize(const Texture& texture) { } } - GPUKTXPayload keyval; - keyval._samplerDesc = texture.getSampler().getDesc(); - keyval._usage = texture.getUsage(); - keyval._usageType = texture.getUsageType(); + GPUKTXPayload gpuKeyval; + gpuKeyval._samplerDesc = texture.getSampler().getDesc(); + gpuKeyval._usage = texture.getUsage(); + gpuKeyval._usageType = texture.getUsageType(); + Byte keyvalPayload[GPUKTXPayload::SIZE]; - keyval.serialize(keyvalPayload); + gpuKeyval.serialize(keyvalPayload); ktx::KeyValues keyValues; keyValues.emplace_back(GPUKTXPayload::KEY, (uint32)GPUKTXPayload::SIZE, (ktx::Byte*) &keyvalPayload); + if (texture.getIrradiance()) { + IrradianceKTXPayload irradianceKeyval; + irradianceKeyval._irradianceSH = *texture.getIrradiance(); + + Byte irradianceKeyvalPayload[IrradianceKTXPayload::SIZE]; + irradianceKeyval.serialize(irradianceKeyvalPayload); + + keyValues.emplace_back(IrradianceKTXPayload::KEY, (uint32)IrradianceKTXPayload::SIZE, (ktx::Byte*) &irradianceKeyvalPayload); + } + auto hash = texture.sourceHash(); if (!hash.empty()) { // the sourceHash is an std::string in hex @@ -409,6 +475,12 @@ TexturePointer Texture::unserialize(const std::string& ktxfile, const ktx::KTXDe // Assing the mips availables texture->setStoredMipFormat(mipFormat); texture->setKtxBacking(ktxfile); + + IrradianceKTXPayload irradianceKtxKeyValue; + if (IrradianceKTXPayload::findInKeyValues(descriptor.keyValues, irradianceKtxKeyValue)) { + texture->overrideIrradiance(std::make_shared(irradianceKtxKeyValue._irradianceSH)); + } + return texture; } From c7f5115e26a6cdc7e6e33649c475ff7f39550105 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 3 May 2017 16:38:25 -0700 Subject: [PATCH 15/26] Move help text to above buttons --- scripts/system/html/css/SnapshotReview.css | 4 ++-- scripts/system/html/js/SnapshotReview.js | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/system/html/css/SnapshotReview.css b/scripts/system/html/css/SnapshotReview.css index 29c5f465d7..bd9bb81fdc 100644 --- a/scripts/system/html/css/SnapshotReview.css +++ b/scripts/system/html/css/SnapshotReview.css @@ -158,7 +158,7 @@ input[type=button].naked:active { align-items: flex-end; height: 40px; width: calc(100% - 60px); - margin-bottom: 25px; + margin-bottom: -24px; margin-left: 0; } .shareButtons img { @@ -178,7 +178,7 @@ input[type=button].naked:active { height: 25px; line-height: 25px; position: absolute; - bottom: 0; + bottom: 40px; left: 73px; right: 0; font-family: Raleway-Regular; diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index 6365c2be18..a8a73b62bc 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -134,7 +134,8 @@ function createShareBar(parentID, isGif, blastButtonDisabled, hifiButtonDisabled shareBar.id = parentID + "shareBar"; shareBar.className = "shareControls"; - var shareBarInnerHTML = '
' + + var shareBarInnerHTML = '' + + '
' + '' + '' + '' + @@ -151,8 +152,6 @@ function createShareBar(parentID, isGif, blastButtonDisabled, hifiButtonDisabled shareBar.innerHTML = shareBarInnerHTML; - shareBar.innerHTML += ''; - // Add onclick handler to parent DIV's img to toggle share buttons document.getElementById(parentID + 'img').onclick = function () { selectImageToShare(parentID, true); }; From b01bff061e7a6f0122eb3b6562e6f162628ff787 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 4 May 2017 10:06:33 -0700 Subject: [PATCH 16/26] Hover lock when uploading --- scripts/system/html/js/SnapshotReview.js | 99 +++++++++++++----------- 1 file changed, 52 insertions(+), 47 deletions(-) diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index a8a73b62bc..4aede9e3bc 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -261,6 +261,7 @@ function showUploadingMessage(selectedID, destination) { var shareBarHelp = document.getElementById(selectedID + "shareBarHelp"); shareBarHelp.innerHTML = 'Preparing to Share'; + shareBarHelp.classList.add("uploading"); shareBarHelp.setAttribute("data-destination", destination); } function hideUploadingMessageAndShare(selectedID, storyID) { @@ -270,6 +271,8 @@ function hideUploadingMessageAndShare(selectedID, storyID) { var shareBarHelp = document.getElementById(selectedID + "shareBarHelp"), shareBarHelpDestination = shareBarHelp.getAttribute("data-destination"); + + shareBarHelp.classList.remove("uploading"); if (shareBarHelpDestination) { switch (shareBarHelpDestination) { case 'blast': @@ -387,54 +390,56 @@ function shareButtonHovered(destination, selectedID) { shareButtonsDiv = document.getElementById(selectedID + "shareButtonsDiv").childNodes, itr; - for (itr = 0; itr < shareButtonsDiv.length; itr += 1) { - shareButtonsDiv[itr].style.backgroundColor = "rgba(0, 0, 0, 0)"; - } - shareBarHelp.style.opacity = "1.0"; + if (!shareBarHelp.classList.contains("uploading")) { - switch (destination) { - case 'blast': - var blastToConnectionsButton = document.getElementById(selectedID + "blastToConnectionsButton"); - if (!blastToConnectionsButton.classList.contains("disabled")) { - shareBarHelp.style.backgroundColor = "#EA4C5F"; - shareBarHelp.style.opacity = "1.0"; - blastToConnectionsButton.style.backgroundColor = "#EA4C5F"; - blastToConnectionsButton.style.opacity = "1.0"; - shareBarHelp.innerHTML = blastShareText; - } else { - shareBarHelp.style.backgroundColor = "#000000"; - shareBarHelp.style.opacity = "0.5"; - blastToConnectionsButton.style.backgroundColor = "#000000"; - blastToConnectionsButton.style.opacity = "0.5"; - shareBarHelp.innerHTML = blastAlreadySharedText; - } - break; - case 'hifi': - var shareWithEveryoneButton = document.getElementById(selectedID + "shareWithEveryoneButton"); - if (!shareWithEveryoneButton.classList.contains("disabled")) { - shareBarHelp.style.backgroundColor = "#1FC6A6"; - shareBarHelp.style.opacity = "1.0"; - shareWithEveryoneButton.style.backgroundColor = "#1FC6A6"; - shareWithEveryoneButton.style.opacity = "1.0"; - shareBarHelp.innerHTML = hifiShareText; - } else { - shareBarHelp.style.backgroundColor = "#000000"; - shareBarHelp.style.opacity = "0.5"; - shareWithEveryoneButton.style.backgroundColor = "#000000"; - shareWithEveryoneButton.style.opacity = "0.5"; - shareBarHelp.innerHTML = hifiAlreadySharedText; - } - break; - case 'facebook': - shareBarHelp.style.backgroundColor = "#3C58A0"; - shareBarHelp.innerHTML = facebookShareText; - document.getElementById(selectedID + "facebookButton").style.backgroundColor = "#3C58A0"; - break; - case 'twitter': - shareBarHelp.style.backgroundColor = "#00B4EE"; - shareBarHelp.innerHTML = twitterShareText; - document.getElementById(selectedID + "twitterButton").style.backgroundColor = "#00B4EE"; - break; + for (itr = 0; itr < shareButtonsDiv.length; itr += 1) { + shareButtonsDiv[itr].style.backgroundColor = "rgba(0, 0, 0, 0)"; + } + shareBarHelp.style.opacity = "1.0"; + switch (destination) { + case 'blast': + var blastToConnectionsButton = document.getElementById(selectedID + "blastToConnectionsButton"); + if (!blastToConnectionsButton.classList.contains("disabled")) { + shareBarHelp.style.backgroundColor = "#EA4C5F"; + shareBarHelp.style.opacity = "1.0"; + blastToConnectionsButton.style.backgroundColor = "#EA4C5F"; + blastToConnectionsButton.style.opacity = "1.0"; + shareBarHelp.innerHTML = blastShareText; + } else { + shareBarHelp.style.backgroundColor = "#000000"; + shareBarHelp.style.opacity = "0.5"; + blastToConnectionsButton.style.backgroundColor = "#000000"; + blastToConnectionsButton.style.opacity = "0.5"; + shareBarHelp.innerHTML = blastAlreadySharedText; + } + break; + case 'hifi': + var shareWithEveryoneButton = document.getElementById(selectedID + "shareWithEveryoneButton"); + if (!shareWithEveryoneButton.classList.contains("disabled")) { + shareBarHelp.style.backgroundColor = "#1FC6A6"; + shareBarHelp.style.opacity = "1.0"; + shareWithEveryoneButton.style.backgroundColor = "#1FC6A6"; + shareWithEveryoneButton.style.opacity = "1.0"; + shareBarHelp.innerHTML = hifiShareText; + } else { + shareBarHelp.style.backgroundColor = "#000000"; + shareBarHelp.style.opacity = "0.5"; + shareWithEveryoneButton.style.backgroundColor = "#000000"; + shareWithEveryoneButton.style.opacity = "0.5"; + shareBarHelp.innerHTML = hifiAlreadySharedText; + } + break; + case 'facebook': + shareBarHelp.style.backgroundColor = "#3C58A0"; + shareBarHelp.innerHTML = facebookShareText; + document.getElementById(selectedID + "facebookButton").style.backgroundColor = "#3C58A0"; + break; + case 'twitter': + shareBarHelp.style.backgroundColor = "#00B4EE"; + shareBarHelp.innerHTML = twitterShareText; + document.getElementById(selectedID + "twitterButton").style.backgroundColor = "#00B4EE"; + break; + } } } function shareButtonClicked(destination, selectedID) { From b08d23924a58a9134bd82a05d820389324652036 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 4 May 2017 11:00:13 -0700 Subject: [PATCH 17/26] Keep hover status of both images in sync --- scripts/system/html/js/SnapshotReview.js | 31 +++++++++++++++--------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index 4aede9e3bc..27062faea2 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -143,11 +143,11 @@ function createShareBar(parentID, isGif, blastButtonDisabled, hifiButtonDisabled '
' + ''; shareBar.innerHTML = shareBarInnerHTML; @@ -164,11 +164,11 @@ function appendShareBar(divID, isGif, blastButtonDisabled, hifiButtonDisabled, c document.getElementById(divID).appendChild(createShareBar(divID, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast)); if (divID === "p0") { selectImageToShare(divID, true); - if (canBlast) { - shareButtonHovered('blast', divID); - } else { - shareButtonHovered('hifi', divID); - } + } + if (canBlast) { + shareButtonHovered('blast', divID, false); + } else { + shareButtonHovered('hifi', divID, false); } } function shareForUrl(selectedID) { @@ -382,7 +382,7 @@ function shareWithEveryone(selectedID, isGif) { showUploadingMessage(selectedID, 'hifi'); } } -function shareButtonHovered(destination, selectedID) { +function shareButtonHovered(destination, selectedID, shouldAlsoModifyOther) { if (selectedID.id) { selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID } @@ -391,7 +391,6 @@ function shareButtonHovered(destination, selectedID) { itr; if (!shareBarHelp.classList.contains("uploading")) { - for (itr = 0; itr < shareButtonsDiv.length; itr += 1) { shareButtonsDiv[itr].style.backgroundColor = "rgba(0, 0, 0, 0)"; } @@ -441,6 +440,14 @@ function shareButtonHovered(destination, selectedID) { break; } } + + if (shouldAlsoModifyOther && imageCount > 1) { + if (selectedID === "p0" && !document.getElementById("p1").classList.contains("processingGif")) { + shareButtonHovered(destination, "p1", false); + } else if (selectedID === "p1") { + shareButtonHovered(destination, "p0", false); + } + } } function shareButtonClicked(destination, selectedID) { if (selectedID.id) { @@ -525,6 +532,7 @@ window.onload = function () { message.image_data.forEach(function (element, idx) { addImage(element, idx === 1, idx === 0 && messageOptions.canShare, false); }); + document.getElementById("p1").classList.add("processingGif"); } else { var gifPath = message.image_data[0].localPath, p1img = document.getElementById('p1img'); @@ -534,6 +542,7 @@ window.onload = function () { if (messageOptions.canShare) { shareForUrl("p1"); appendShareBar("p1", true, false, false, true); + document.getElementById("p1").classList.remove("processingGif"); } } } else { From fb995bebf17a3a92c6eacf70bf07b7cad2e6609c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 4 May 2017 12:04:21 -0700 Subject: [PATCH 18/26] Prevent crash on switching to 3D display twice --- libraries/ui/src/VrMenu.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/libraries/ui/src/VrMenu.cpp b/libraries/ui/src/VrMenu.cpp index 73eb32ce17..878514dd41 100644 --- a/libraries/ui/src/VrMenu.cpp +++ b/libraries/ui/src/VrMenu.cpp @@ -37,15 +37,17 @@ public: qmlObject->setObjectName(uuid.toString()); // Make sure we can find it again in the future updateQmlItemFromAction(); - auto connection = QObject::connect(action, &QAction::changed, [=] { + _changedConnection = QObject::connect(action, &QAction::changed, [=] { updateQmlItemFromAction(); }); - QObject::connect(qApp, &QCoreApplication::aboutToQuit, [=] { - QObject::disconnect(connection); + _shutdownConnection = QObject::connect(qApp, &QCoreApplication::aboutToQuit, [=] { + QObject::disconnect(_changedConnection); }); } ~MenuUserData() { + QObject::disconnect(_changedConnection); + QObject::disconnect(_shutdownConnection); _action->setUserData(USER_DATA_ID, nullptr); _qml->setUserData(USER_DATA_ID, nullptr); } @@ -104,6 +106,8 @@ public: private: MenuUserData(const MenuUserData&); + QMetaObject::Connection _shutdownConnection; + QMetaObject::Connection _changedConnection; QAction* _action { nullptr }; QObject* _qml { nullptr }; }; From e87f8dfe45fd76e8d16e30e3ea5ec0cf5430caec Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 4 May 2017 13:31:24 -0700 Subject: [PATCH 19/26] fix issue with not seeing your own attachments --- interface/src/avatar/Avatar.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 3bd4c663d2..49ab862acf 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -525,13 +525,11 @@ static TextRenderer3D* textRenderer(TextRendererType type) { void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { auto avatarPayload = new render::Payload(self); auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload); - if (_skeletonModel->addToScene(scene, transaction)) { - _renderItemID = scene->allocateID(); - transaction.resetItem(_renderItemID, avatarPayloadPointer); - - for (auto& attachmentModel : _attachmentModels) { - attachmentModel->addToScene(scene, transaction); - } + _renderItemID = scene->allocateID(); + transaction.resetItem(_renderItemID, avatarPayloadPointer); + _skeletonModel->addToScene(scene, transaction); + for (auto& attachmentModel : _attachmentModels) { + attachmentModel->addToScene(scene, transaction); } } From 2d0bbf70ae06f9d31a33050da7eb0e4ea5a176bb Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 27 Apr 2017 14:42:15 -0700 Subject: [PATCH 20/26] Migrate core avatar rendering functionality to library --- interface/CMakeLists.txt | 2 +- interface/src/Application.cpp | 32 +++- interface/src/Application.h | 1 + interface/src/FancyCamera.cpp | 3 + interface/src/FancyCamera.h | 4 +- interface/src/Util.cpp | 5 - interface/src/Util.h | 3 - interface/src/avatar/AvatarManager.cpp | 4 +- interface/src/avatar/AvatarManager.h | 6 +- interface/src/avatar/MyAvatar.cpp | 40 +++-- interface/src/avatar/MyAvatar.h | 21 ++- interface/src/avatar/MyHead.cpp | 76 +++++++++ interface/src/avatar/MyHead.h | 30 ++++ interface/src/avatar/MySkeletonModel.cpp | 158 ++++++++++++++++++ interface/src/avatar/MySkeletonModel.h | 26 +++ interface/src/devices/DdeFaceTracker.cpp | 15 +- interface/src/devices/DdeFaceTracker.h | 2 + interface/src/devices/Leapmotion.cpp | 6 +- interface/src/devices/Leapmotion.h | 3 - libraries/avatars-renderer/CMakeLists.txt | 2 +- .../src/avatars-renderer}/Avatar.cpp | 63 +++---- .../src/avatars-renderer}/Avatar.h | 23 +-- .../avatars-renderer}/AvatarMotionState.cpp | 5 +- .../src/avatars-renderer}/AvatarMotionState.h | 3 +- .../src/avatars-renderer}/Head.cpp | 112 ++----------- .../src/avatars-renderer}/Head.h | 63 +++---- .../Logging.cpp} | 2 +- .../Logging.h} | 0 .../src/avatars-renderer/OtherAvatar.cpp | 16 ++ .../src/avatars-renderer/OtherAvatar.h | 20 +++ .../src/avatars-renderer}/ScriptAvatar.cpp | 0 .../src/avatars-renderer}/ScriptAvatar.h | 0 .../src/avatars-renderer}/SkeletonModel.cpp | 148 ++-------------- .../src/avatars-renderer}/SkeletonModel.h | 2 +- libraries/avatars/src/AvatarData.h | 4 +- libraries/avatars/src/HeadData.cpp | 5 - .../src}/CauterizedMeshPartPayload.cpp | 4 +- .../src}/CauterizedMeshPartPayload.h | 7 +- .../render-utils/src}/CauterizedModel.cpp | 7 +- .../render-utils/src}/CauterizedModel.h | 5 +- .../render-utils/src}/SoftAttachmentModel.cpp | 4 - .../render-utils/src}/SoftAttachmentModel.h | 3 - libraries/shared/src/GLMHelpers.cpp | 6 + libraries/shared/src/GLMHelpers.h | 3 + .../shared/src/shared}/Camera.cpp | 35 ---- .../shared/src/shared}/Camera.h | 10 +- tests/render-perf/src/Camera.hpp | 4 +- tests/render-perf/src/main.cpp | 4 +- 48 files changed, 530 insertions(+), 467 deletions(-) create mode 100644 interface/src/avatar/MyHead.cpp create mode 100644 interface/src/avatar/MyHead.h create mode 100644 interface/src/avatar/MySkeletonModel.cpp create mode 100644 interface/src/avatar/MySkeletonModel.h rename {interface/src/avatar => libraries/avatars-renderer/src/avatars-renderer}/Avatar.cpp (96%) rename {interface/src/avatar => libraries/avatars-renderer/src/avatars-renderer}/Avatar.h (96%) rename {interface/src/avatar => libraries/avatars-renderer/src/avatars-renderer}/AvatarMotionState.cpp (99%) rename {interface/src/avatar => libraries/avatars-renderer/src/avatars-renderer}/AvatarMotionState.h (98%) rename {interface/src/avatar => libraries/avatars-renderer/src/avatars-renderer}/Head.cpp (77%) rename {interface/src/avatar => libraries/avatars-renderer/src/avatars-renderer}/Head.h (79%) rename libraries/avatars-renderer/src/{AvatarsRendererLogging.cpp => avatars-renderer/Logging.cpp} (89%) rename libraries/avatars-renderer/src/{AvatarsRendererLogging.h => avatars-renderer/Logging.h} (100%) create mode 100644 libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.cpp create mode 100644 libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.h rename {interface/src/avatar => libraries/avatars-renderer/src/avatars-renderer}/ScriptAvatar.cpp (100%) rename {interface/src/avatar => libraries/avatars-renderer/src/avatars-renderer}/ScriptAvatar.h (100%) rename {interface/src/avatar => libraries/avatars-renderer/src/avatars-renderer}/SkeletonModel.cpp (66%) rename {interface/src/avatar => libraries/avatars-renderer/src/avatars-renderer}/SkeletonModel.h (99%) rename {interface/src/avatar => libraries/render-utils/src}/CauterizedMeshPartPayload.cpp (94%) rename {interface/src/avatar => libraries/render-utils/src}/CauterizedMeshPartPayload.h (87%) rename {interface/src/avatar => libraries/render-utils/src}/CauterizedModel.cpp (98%) rename {interface/src/avatar => libraries/render-utils/src}/CauterizedModel.h (95%) rename {interface/src/avatar => libraries/render-utils/src}/SoftAttachmentModel.cpp (97%) rename {interface/src/avatar => libraries/render-utils/src}/SoftAttachmentModel.h (95%) rename {interface/src => libraries/shared/src/shared}/Camera.cpp (79%) rename {interface/src => libraries/shared/src/shared}/Camera.h (94%) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index d7e4b1ae7c..71341f3f11 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -191,7 +191,7 @@ endif() # link required hifi libraries link_hifi_libraries( shared octree ktx gpu gl gpu-gl procedural model render - recording fbx networking model-networking entities avatars + recording fbx networking model-networking entities avatars trackers audio audio-client animation script-engine physics render-utils entities-renderer avatars-renderer ui auto-updater controllers plugins image trackers diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1e3df955ba..1585d41115 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -129,12 +129,12 @@ #include #include #include - +#include #include "AudioClient.h" #include "audio/AudioScope.h" #include "avatar/AvatarManager.h" -#include "avatar/ScriptAvatar.h" +#include "avatar/MyHead.h" #include "CrashHandler.h" #include "devices/DdeFaceTracker.h" #include "devices/Leapmotion.h" @@ -1586,6 +1586,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(&domainHandler, &DomainHandler::hostnameChanged, this, &Application::addAssetToWorldMessageClose); updateSystemTabletMode(); + + connect(&_myCamera, &Camera::modeUpdated, this, &Application::cameraModeChanged); } void Application::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) { @@ -2191,7 +2193,7 @@ void Application::paintGL() { _myCamera.setOrientation(glm::quat_cast(camMat)); } else { _myCamera.setPosition(myAvatar->getDefaultEyePosition()); - _myCamera.setOrientation(myAvatar->getHead()->getCameraOrientation()); + _myCamera.setOrientation(myAvatar->getMyHead()->getCameraOrientation()); } } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { if (isHMDMode()) { @@ -4082,6 +4084,30 @@ void Application::cycleCamera() { cameraMenuChanged(); // handle the menu change } +void Application::cameraModeChanged() { + switch (_myCamera.getMode()) { + case CAMERA_MODE_FIRST_PERSON: + Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, true); + break; + case CAMERA_MODE_THIRD_PERSON: + Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true); + break; + case CAMERA_MODE_MIRROR: + Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, true); + break; + case CAMERA_MODE_INDEPENDENT: + Menu::getInstance()->setIsOptionChecked(MenuOption::IndependentMode, true); + break; + case CAMERA_MODE_ENTITY: + Menu::getInstance()->setIsOptionChecked(MenuOption::CameraEntityMode, true); + break; + default: + break; + } + cameraMenuChanged(); +} + + void Application::cameraMenuChanged() { if (Menu::getInstance()->isOptionChecked(MenuOption::FullscreenMirror)) { if (_myCamera.getMode() != CAMERA_MODE_MIRROR) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 041f1f8930..cbdfe8aeb1 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -372,6 +372,7 @@ public slots: static void showHelp(); void cycleCamera(); + void cameraModeChanged(); void cameraMenuChanged(); void toggleOverlays(); void setOverlaysVisible(bool visible); diff --git a/interface/src/FancyCamera.cpp b/interface/src/FancyCamera.cpp index 7bb64a4a8e..298cab9948 100644 --- a/interface/src/FancyCamera.cpp +++ b/interface/src/FancyCamera.cpp @@ -12,6 +12,9 @@ #include "Application.h" +PickRay FancyCamera::computePickRay(float x, float y) const { + return qApp->computePickRay(x, y); +} QUuid FancyCamera::getCameraEntity() const { if (_cameraEntity != nullptr) { diff --git a/interface/src/FancyCamera.h b/interface/src/FancyCamera.h index 66f7a07dbd..3552dc6ca8 100644 --- a/interface/src/FancyCamera.h +++ b/interface/src/FancyCamera.h @@ -11,7 +11,7 @@ #ifndef hifi_FancyCamera_h #define hifi_FancyCamera_h -#include "Camera.h" +#include #include @@ -30,6 +30,8 @@ public: FancyCamera() : Camera() {} EntityItemPointer getCameraEntityPointer() const { return _cameraEntity; } + PickRay computePickRay(float x, float y) const override; + public slots: QUuid getCameraEntity() const; diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 7bf48d98d2..78a503bc71 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -142,11 +142,6 @@ void renderWorldBox(gpu::Batch& batch) { geometryCache->renderSolidSphereInstance(batch, GREY); } -// Return a random vector of average length 1 -const glm::vec3 randVector() { - return glm::vec3(randFloat() - 0.5f, randFloat() - 0.5f, randFloat() - 0.5f) * 2.0f; -} - // Do some basic timing tests and report the results void runTimingTests() { // How long does it take to make a call to get the time? diff --git a/interface/src/Util.h b/interface/src/Util.h index 60e38ae0ec..b1b4c78bcb 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -17,9 +17,6 @@ #include -float randFloat(); -const glm::vec3 randVector(); - void renderWorldBox(gpu::Batch& batch); void runTimingTests(); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 04ab1531ba..1306ce03ea 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -32,9 +32,9 @@ #include #include #include +#include #include "Application.h" -#include "Avatar.h" #include "AvatarManager.h" #include "InterfaceLogging.h" #include "Menu.h" @@ -299,7 +299,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { } AvatarSharedPointer AvatarManager::newSharedAvatar() { - return std::make_shared(qApp->thread(), std::make_shared()); + return std::make_shared(qApp->thread(), std::make_shared()); } void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index c67088a4be..9df1639853 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -21,13 +21,11 @@ #include #include #include +#include +#include -#include "Avatar.h" #include "MyAvatar.h" -#include "AvatarMotionState.h" -#include "ScriptAvatar.h" -class MyAvatar; class AudioInjector; class AvatarManager : public AvatarHashMap { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5e285f21ba..3f3ce7d9e9 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "MyAvatar.h" + #include #include @@ -43,11 +45,12 @@ #include #include +#include "MyHead.h" +#include "MySkeletonModel.h" #include "Application.h" #include "AvatarManager.h" #include "AvatarActionHold.h" #include "Menu.h" -#include "MyAvatar.h" #include "Util.h" #include "InterfaceLogging.h" #include "DebugDraw.h" @@ -96,23 +99,12 @@ static const glm::quat DEFAULT_AVATAR_RIGHTFOOT_ROT { -0.4016716778278351f, 0.91 MyAvatar::MyAvatar(QThread* thread, RigPointer rig) : Avatar(thread, rig), - _wasPushing(false), - _isPushing(false), - _isBeingPushed(false), - _isBraking(false), - _isAway(false), - _boomLength(ZOOM_DEFAULT), _yawSpeed(YAW_SPEED_DEFAULT), _pitchSpeed(PITCH_SPEED_DEFAULT), - _thrust(0.0f), - _actionMotorVelocity(0.0f), - _scriptedMotorVelocity(0.0f), _scriptedMotorTimescale(DEFAULT_SCRIPTED_MOTOR_TIMESCALE), _scriptedMotorFrame(SCRIPTED_MOTOR_CAMERA_FRAME), _motionBehaviors(AVATAR_MOTION_DEFAULTS), _characterController(this), - _lookAtTargetAvatar(), - _shouldRender(true), _eyeContactTarget(LEFT_EYE), _realWorldFieldOfView("realWorldFieldOfView", DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES), @@ -129,6 +121,14 @@ MyAvatar::MyAvatar(QThread* thread, RigPointer rig) : _audioListenerMode(FROM_HEAD), _hmdAtRestDetector(glm::vec3(0), glm::quat()) { + + // give the pointer to our head to inherited _headData variable from AvatarData + _headData = new MyHead(this); + + _skeletonModel = std::make_shared(this, nullptr, rig); + connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); + + using namespace recording; _skeletonModel->flagAsCauterized(); @@ -536,7 +536,7 @@ void MyAvatar::simulate(float deltaTime) { } head->setPosition(headPosition); head->setScale(getUniformScale()); - head->simulate(deltaTime, true); + head->simulate(deltaTime); } // Record avatars movements. @@ -1450,12 +1450,12 @@ void MyAvatar::updateMotors() { glm::quat motorRotation; if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) { if (_characterController.getState() == CharacterController::State::Hover) { - motorRotation = getHead()->getCameraOrientation(); + motorRotation = getMyHead()->getCameraOrientation(); } else { // non-hovering = walking: follow camera twist about vertical but not lift // so we decompose camera's rotation and store the twist part in motorRotation glm::quat liftRotation; - swingTwistDecomposition(getHead()->getCameraOrientation(), _worldUpDirection, liftRotation, motorRotation); + swingTwistDecomposition(getMyHead()->getCameraOrientation(), _worldUpDirection, liftRotation, motorRotation); } const float DEFAULT_MOTOR_TIMESCALE = 0.2f; const float INVALID_MOTOR_TIMESCALE = 1.0e6f; @@ -1469,7 +1469,7 @@ void MyAvatar::updateMotors() { } if (_motionBehaviors & AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED) { if (_scriptedMotorFrame == SCRIPTED_MOTOR_CAMERA_FRAME) { - motorRotation = getHead()->getCameraOrientation() * glm::angleAxis(PI, Vectors::UNIT_Y); + motorRotation = getMyHead()->getCameraOrientation() * glm::angleAxis(PI, Vectors::UNIT_Y); } else if (_scriptedMotorFrame == SCRIPTED_MOTOR_AVATAR_FRAME) { motorRotation = getOrientation() * glm::angleAxis(PI, Vectors::UNIT_Y); } else { @@ -1814,7 +1814,7 @@ void MyAvatar::updateOrientation(float deltaTime) { if (getCharacterController()->getState() == CharacterController::State::Hover) { // This is the direction the user desires to fly in. - glm::vec3 desiredFacing = getHead()->getCameraOrientation() * Vectors::UNIT_Z; + glm::vec3 desiredFacing = getMyHead()->getCameraOrientation() * Vectors::UNIT_Z; desiredFacing.y = 0.0f; // This is our reference frame, it is captured when the user begins to move. @@ -1958,7 +1958,7 @@ void MyAvatar::updatePosition(float deltaTime) { if (!_hoverReferenceCameraFacingIsCaptured && (fabs(getDriveKey(TRANSLATE_Z)) > 0.1f || fabs(getDriveKey(TRANSLATE_X)) > 0.1f)) { _hoverReferenceCameraFacingIsCaptured = true; // transform the camera facing vector into sensor space. - _hoverReferenceCameraFacing = transformVectorFast(glm::inverse(_sensorToWorldMatrix), getHead()->getCameraOrientation() * Vectors::UNIT_Z); + _hoverReferenceCameraFacing = transformVectorFast(glm::inverse(_sensorToWorldMatrix), getMyHead()->getCameraOrientation() * Vectors::UNIT_Z); } else if (_hoverReferenceCameraFacingIsCaptured && (fabs(getDriveKey(TRANSLATE_Z)) <= 0.1f && fabs(getDriveKey(TRANSLATE_X)) <= 0.1f)) { _hoverReferenceCameraFacingIsCaptured = false; } @@ -2804,3 +2804,7 @@ void MyAvatar::updateHoldActions(const AnimPose& prePhysicsPose, const AnimPose& }); } } + +const MyHead* MyAvatar::getMyHead() const { + return static_cast(getHead()); +} diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 6a1e457a97..7c510f0556 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -22,14 +22,15 @@ #include #include +#include -#include "Avatar.h" #include "AtRestDetector.h" #include "MyCharacterController.h" #include class AvatarActionHold; class ModelItemID; +class MyHead; enum eyeContactTarget { LEFT_EYE, @@ -149,6 +150,7 @@ public: explicit MyAvatar(QThread* thread, RigPointer rig); ~MyAvatar(); + void instantiableAvatar() override {}; void registerMetaTypes(QScriptEngine* engine); virtual void simulateAttachments(float deltaTime) override; @@ -353,6 +355,7 @@ public: eyeContactTarget getEyeContactTarget(); + const MyHead* getMyHead() const; Q_INVOKABLE glm::vec3 getHeadPosition() const { return getHead()->getPosition(); } Q_INVOKABLE float getHeadFinalYaw() const { return getHead()->getFinalYaw(); } Q_INVOKABLE float getHeadFinalRoll() const { return getHead()->getFinalRoll(); } @@ -589,17 +592,17 @@ private: std::array _driveKeys; std::bitset _disabledDriveKeys; - bool _wasPushing; - bool _isPushing; - bool _isBeingPushed; - bool _isBraking; - bool _isAway; + bool _wasPushing { false }; + bool _isPushing { false }; + bool _isBeingPushed { false }; + bool _isBraking { false }; + bool _isAway { false }; - float _boomLength; + float _boomLength { ZOOM_DEFAULT }; float _yawSpeed; // degrees/sec float _pitchSpeed; // degrees/sec - glm::vec3 _thrust; // impulse accumulator for outside sources + glm::vec3 _thrust { 0.0f }; // impulse accumulator for outside sources glm::vec3 _actionMotorVelocity; // target local-frame velocity of avatar (default controller actions) glm::vec3 _scriptedMotorVelocity; // target local-frame velocity of avatar (analog script) @@ -615,7 +618,7 @@ private: AvatarWeakPointer _lookAtTargetAvatar; glm::vec3 _targetAvatarPosition; - bool _shouldRender; + bool _shouldRender { true }; float _oculusYawOffset; eyeContactTarget _eyeContactTarget; diff --git a/interface/src/avatar/MyHead.cpp b/interface/src/avatar/MyHead.cpp new file mode 100644 index 0000000000..c41fff3bb5 --- /dev/null +++ b/interface/src/avatar/MyHead.cpp @@ -0,0 +1,76 @@ +// +// Created by Bradley Austin Davis on 2017/04/27 +// Copyright 2013-2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "MyHead.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include "devices/DdeFaceTracker.h" +#include "Application.h" +#include "MyAvatar.h" + +using namespace std; + +MyHead::MyHead(MyAvatar* owningAvatar) : Head(owningAvatar) { +} + +glm::quat MyHead::getCameraOrientation() const { + // NOTE: Head::getCameraOrientation() is not used for orienting the camera "view" while in Oculus mode, so + // you may wonder why this code is here. This method will be called while in Oculus mode to determine how + // to change the driving direction while in Oculus mode. It is used to support driving toward where you're + // head is looking. Note that in oculus mode, your actual camera view and where your head is looking is not + // always the same. + if (qApp->isHMDMode()) { + MyAvatar* myAvatar = static_cast(_owningAvatar); + return glm::quat_cast(myAvatar->getSensorToWorldMatrix()) * myAvatar->getHMDSensorOrientation(); + } else { + Avatar* owningAvatar = static_cast(_owningAvatar); + return owningAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, 0.0f, 0.0f))); + } +} + +void MyHead::simulate(float deltaTime) { + auto player = DependencyManager::get(); + // Only use face trackers when not playing back a recording. + if (!player->isPlaying()) { + FaceTracker* faceTracker = qApp->getActiveFaceTracker(); + _isFaceTrackerConnected = faceTracker != NULL && !faceTracker->isMuted(); + if (_isFaceTrackerConnected) { + _blendshapeCoefficients = faceTracker->getBlendshapeCoefficients(); + + if (typeid(*faceTracker) == typeid(DdeFaceTracker)) { + + if (Menu::getInstance()->isOptionChecked(MenuOption::UseAudioForMouth)) { + calculateMouthShapes(deltaTime); + + const int JAW_OPEN_BLENDSHAPE = 21; + const int MMMM_BLENDSHAPE = 34; + const int FUNNEL_BLENDSHAPE = 40; + const int SMILE_LEFT_BLENDSHAPE = 28; + const int SMILE_RIGHT_BLENDSHAPE = 29; + _blendshapeCoefficients[JAW_OPEN_BLENDSHAPE] += _audioJawOpen; + _blendshapeCoefficients[SMILE_LEFT_BLENDSHAPE] += _mouth4; + _blendshapeCoefficients[SMILE_RIGHT_BLENDSHAPE] += _mouth4; + _blendshapeCoefficients[MMMM_BLENDSHAPE] += _mouth2; + _blendshapeCoefficients[FUNNEL_BLENDSHAPE] += _mouth3; + } + applyEyelidOffset(getFinalOrientationInWorldFrame()); + } + } + auto eyeTracker = DependencyManager::get(); + _isEyeTrackerConnected = eyeTracker->isTracking(); + } + Parent::simulate(deltaTime); +} \ No newline at end of file diff --git a/interface/src/avatar/MyHead.h b/interface/src/avatar/MyHead.h new file mode 100644 index 0000000000..097415153c --- /dev/null +++ b/interface/src/avatar/MyHead.h @@ -0,0 +1,30 @@ +// +// Created by Bradley Austin Davis on 2017/04/27 +// Copyright 2013-2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_MyHead_h +#define hifi_MyHead_h + +#include + +class MyAvatar; +class MyHead : public Head { + using Parent = Head; +public: + explicit MyHead(MyAvatar* owningAvatar); + + /// \return orientationBody * orientationBasePitch + glm::quat getCameraOrientation() const; + void simulate(float deltaTime) override; + +private: + // disallow copies of the Head, copy of owning Avatar is disallowed too + MyHead(const Head&); + MyHead& operator= (const MyHead&); +}; + +#endif // hifi_MyHead_h diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp new file mode 100644 index 0000000000..639c9f003f --- /dev/null +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -0,0 +1,158 @@ +// +// Created by Bradley Austin Davis on 2017/04/27 +// Copyright 2013-2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "MySkeletonModel.h" + +#include + +#include "Application.h" +#include "InterfaceLogging.h" + +MySkeletonModel::MySkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) : SkeletonModel(owningAvatar, parent, rig) { +} + +Rig::CharacterControllerState convertCharacterControllerState(CharacterController::State state) { + switch (state) { + default: + case CharacterController::State::Ground: + return Rig::CharacterControllerState::Ground; + case CharacterController::State::Takeoff: + return Rig::CharacterControllerState::Takeoff; + case CharacterController::State::InAir: + return Rig::CharacterControllerState::InAir; + case CharacterController::State::Hover: + return Rig::CharacterControllerState::Hover; + }; +} + +// Called within Model::simulate call, below. +void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { + const FBXGeometry& geometry = getFBXGeometry(); + + Head* head = _owningAvatar->getHead(); + + // make sure lookAt is not too close to face (avoid crosseyes) + glm::vec3 lookAt = _owningAvatar->isMyAvatar() ? head->getLookAtPosition() : head->getCorrectedLookAtPosition(); + MyAvatar* myAvatar = static_cast(_owningAvatar); + + Rig::HeadParameters headParams; + + // input action is the highest priority source for head orientation. + auto avatarHeadPose = myAvatar->getHeadControllerPoseInAvatarFrame(); + if (avatarHeadPose.isValid()) { + glm::mat4 rigHeadMat = Matrices::Y_180 * createMatFromQuatAndPos(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation()); + headParams.rigHeadPosition = extractTranslation(rigHeadMat); + headParams.rigHeadOrientation = glmExtractRotation(rigHeadMat); + headParams.headEnabled = true; + } else { + if (qApp->isHMDMode()) { + // get HMD position from sensor space into world space, and back into rig space + glm::mat4 worldHMDMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); + glm::mat4 rigToWorld = createMatFromQuatAndPos(getRotation(), getTranslation()); + glm::mat4 worldToRig = glm::inverse(rigToWorld); + glm::mat4 rigHMDMat = worldToRig * worldHMDMat; + _rig->computeHeadFromHMD(AnimPose(rigHMDMat), headParams.rigHeadPosition, headParams.rigHeadOrientation); + headParams.headEnabled = true; + } else { + // even though full head IK is disabled, the rig still needs the head orientation to rotate the head up and down in desktop mode. + // preMult 180 is necessary to convert from avatar to rig coordinates. + // postMult 180 is necessary to convert head from -z forward to z forward. + headParams.rigHeadOrientation = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame() * Quaternions::Y_180; + headParams.headEnabled = false; + } + } + + auto avatarHipsPose = myAvatar->getHipsControllerPoseInAvatarFrame(); + if (avatarHipsPose.isValid()) { + glm::mat4 rigHipsMat = Matrices::Y_180 * createMatFromQuatAndPos(avatarHipsPose.getRotation(), avatarHipsPose.getTranslation()); + headParams.hipsMatrix = rigHipsMat; + headParams.hipsEnabled = true; + } else { + headParams.hipsEnabled = false; + } + + auto avatarSpine2Pose = myAvatar->getSpine2ControllerPoseInAvatarFrame(); + if (avatarSpine2Pose.isValid()) { + glm::mat4 rigSpine2Mat = Matrices::Y_180 * createMatFromQuatAndPos(avatarSpine2Pose.getRotation(), avatarSpine2Pose.getTranslation()); + headParams.spine2Matrix = rigSpine2Mat; + headParams.spine2Enabled = true; + } else { + headParams.spine2Enabled = false; + } + + headParams.isTalking = head->getTimeWithoutTalking() <= 1.5f; + + _rig->updateFromHeadParameters(headParams, deltaTime); + + Rig::HandAndFeetParameters handAndFeetParams; + + auto leftPose = myAvatar->getLeftHandControllerPoseInAvatarFrame(); + if (leftPose.isValid()) { + handAndFeetParams.isLeftEnabled = true; + handAndFeetParams.leftPosition = Quaternions::Y_180 * leftPose.getTranslation(); + handAndFeetParams.leftOrientation = Quaternions::Y_180 * leftPose.getRotation(); + } else { + handAndFeetParams.isLeftEnabled = false; + } + + auto rightPose = myAvatar->getRightHandControllerPoseInAvatarFrame(); + if (rightPose.isValid()) { + handAndFeetParams.isRightEnabled = true; + handAndFeetParams.rightPosition = Quaternions::Y_180 * rightPose.getTranslation(); + handAndFeetParams.rightOrientation = Quaternions::Y_180 * rightPose.getRotation(); + } else { + handAndFeetParams.isRightEnabled = false; + } + + auto leftFootPose = myAvatar->getLeftFootControllerPoseInAvatarFrame(); + if (leftFootPose.isValid()) { + handAndFeetParams.isLeftFootEnabled = true; + handAndFeetParams.leftFootPosition = Quaternions::Y_180 * leftFootPose.getTranslation(); + handAndFeetParams.leftFootOrientation = Quaternions::Y_180 * leftFootPose.getRotation(); + } else { + handAndFeetParams.isLeftFootEnabled = false; + } + + auto rightFootPose = myAvatar->getRightFootControllerPoseInAvatarFrame(); + if (rightFootPose.isValid()) { + handAndFeetParams.isRightFootEnabled = true; + handAndFeetParams.rightFootPosition = Quaternions::Y_180 * rightFootPose.getTranslation(); + handAndFeetParams.rightFootOrientation = Quaternions::Y_180 * rightFootPose.getRotation(); + } else { + handAndFeetParams.isRightFootEnabled = false; + } + + handAndFeetParams.bodyCapsuleRadius = myAvatar->getCharacterController()->getCapsuleRadius(); + handAndFeetParams.bodyCapsuleHalfHeight = myAvatar->getCharacterController()->getCapsuleHalfHeight(); + handAndFeetParams.bodyCapsuleLocalOffset = myAvatar->getCharacterController()->getCapsuleLocalOffset(); + + _rig->updateFromHandAndFeetParameters(handAndFeetParams, deltaTime); + + Rig::CharacterControllerState ccState = convertCharacterControllerState(myAvatar->getCharacterController()->getState()); + + auto velocity = myAvatar->getLocalVelocity(); + auto position = myAvatar->getLocalPosition(); + auto orientation = myAvatar->getLocalOrientation(); + _rig->computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState); + + // evaluate AnimGraph animation and update jointStates. + Model::updateRig(deltaTime, parentTransform); + + Rig::EyeParameters eyeParams; + eyeParams.eyeLookAt = lookAt; + eyeParams.eyeSaccade = head->getSaccade(); + eyeParams.modelRotation = getRotation(); + eyeParams.modelTranslation = getTranslation(); + eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex; + eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex; + + _rig->updateFromEyeParameters(eyeParams); + + Parent::updateRig(deltaTime, parentTransform); +} + diff --git a/interface/src/avatar/MySkeletonModel.h b/interface/src/avatar/MySkeletonModel.h new file mode 100644 index 0000000000..84fccc825a --- /dev/null +++ b/interface/src/avatar/MySkeletonModel.h @@ -0,0 +1,26 @@ +// +// Created by Bradley Austin Davis on 2017/04/27 +// Copyright 2013-2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_MySkeletonModel_h +#define hifi_MySkeletonModel_h + +#include + +/// A skeleton loaded from a model. +class MySkeletonModel : public SkeletonModel { + Q_OBJECT + +private: + using Parent = SkeletonModel; + +public: + MySkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr, RigPointer rig = nullptr); + void updateRig(float deltaTime, glm::mat4 parentTransform) override; +}; + +#endif // hifi_MySkeletonModel_h diff --git a/interface/src/devices/DdeFaceTracker.cpp b/interface/src/devices/DdeFaceTracker.cpp index fa7b2c173e..ed52083d77 100644 --- a/interface/src/devices/DdeFaceTracker.cpp +++ b/interface/src/devices/DdeFaceTracker.cpp @@ -9,20 +9,21 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "DdeFaceTracker.h" + #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include +#include #include "Application.h" -#include "DdeFaceTracker.h" -#include "FaceshiftConstants.h" #include "InterfaceLogging.h" #include "Menu.h" diff --git a/interface/src/devices/DdeFaceTracker.h b/interface/src/devices/DdeFaceTracker.h index f125dfc3cf..dfb9c6d638 100644 --- a/interface/src/devices/DdeFaceTracker.h +++ b/interface/src/devices/DdeFaceTracker.h @@ -12,6 +12,8 @@ #ifndef hifi_DdeFaceTracker_h #define hifi_DdeFaceTracker_h +#include + #if defined(Q_OS_WIN) || defined(Q_OS_OSX) #define HAVE_DDE #endif diff --git a/interface/src/devices/Leapmotion.cpp b/interface/src/devices/Leapmotion.cpp index cb99cf324d..c643042300 100644 --- a/interface/src/devices/Leapmotion.cpp +++ b/interface/src/devices/Leapmotion.cpp @@ -1,7 +1,4 @@ // -// Leapmotion.cpp -// interface/src/devices -// // Created by Sam Cake on 6/2/2014 // Copyright 2014 High Fidelity, Inc. // @@ -10,10 +7,11 @@ // #include "Leapmotion.h" -#include "Menu.h" #include +#include "Menu.h" + const int PALMROOT_NUM_JOINTS = 3; const int FINGER_NUM_JOINTS = 4; const int HAND_NUM_JOINTS = FINGER_NUM_JOINTS*5+PALMROOT_NUM_JOINTS; diff --git a/interface/src/devices/Leapmotion.h b/interface/src/devices/Leapmotion.h index 6ecec8ccf9..a119821846 100644 --- a/interface/src/devices/Leapmotion.h +++ b/interface/src/devices/Leapmotion.h @@ -1,7 +1,4 @@ // -// Leapmotion.h -// interface/src/devices -// // Created by Sam Cake on 6/2/2014 // Copyright 2014 High Fidelity, Inc. // diff --git a/libraries/avatars-renderer/CMakeLists.txt b/libraries/avatars-renderer/CMakeLists.txt index bee2cb31d6..b13bc0a4a6 100644 --- a/libraries/avatars-renderer/CMakeLists.txt +++ b/libraries/avatars-renderer/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME avatars-renderer) AUTOSCRIBE_SHADER_LIB(gpu model render render-utils) setup_hifi_library(Widgets Network Script) -link_hifi_libraries(shared gpu model animation physics model-networking script-engine render render-utils) +link_hifi_libraries(shared gpu model animation physics model-networking script-engine render image render-utils) target_bullet() diff --git a/interface/src/avatar/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp similarity index 96% rename from interface/src/avatar/Avatar.cpp rename to libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 3bd4c663d2..6408807670 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -27,16 +27,13 @@ #include #include #include +#include +#include -#include "AvatarMotionState.h" -#include "Camera.h" -#include "InterfaceLogging.h" -#include "SceneScriptingInterface.h" -#include "SoftAttachmentModel.h" +#include "Logging.h" using namespace std; -const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f); const int NUM_BODY_CONE_SIDES = 9; const float CHAT_MESSAGE_SCALE = 0.0015f; const float CHAT_MESSAGE_HEIGHT = 0.1f; @@ -71,6 +68,11 @@ namespace render { } } +bool showAvatars { true }; +void Avatar::setShowAvatars(bool render) { + showAvatars = render; +} + static bool showReceiveStats = false; void Avatar::setShowReceiveStats(bool receiveStats) { showReceiveStats = receiveStats; @@ -97,25 +99,6 @@ void Avatar::setShowNamesAboveHeads(bool show) { } Avatar::Avatar(QThread* thread, RigPointer rig) : - AvatarData(), - _skeletonOffset(0.0f), - _bodyYawDelta(0.0f), - _positionDeltaAccumulator(0.0f), - _lastVelocity(0.0f), - _acceleration(0.0f), - _lastAngularVelocity(0.0f), - _lastOrientation(), - _worldUpDirection(DEFAULT_UP_DIRECTION), - _moving(false), - _smoothPositionTime(SMOOTH_TIME_POSITION), - _smoothPositionTimer(std::numeric_limits::max()), - _smoothOrientationTime(SMOOTH_TIME_ORIENTATION), - _smoothOrientationTimer(std::numeric_limits::max()), - _smoothPositionInitial(), - _smoothPositionTarget(), - _smoothOrientationInitial(), - _smoothOrientationTarget(), - _initialized(false), _voiceSphereID(GeometryCache::UNKNOWN_ID) { // we may have been created in the network thread, but we live in the main thread @@ -123,12 +106,6 @@ Avatar::Avatar(QThread* thread, RigPointer rig) : setScale(glm::vec3(1.0f)); // avatar scale is uniform - // give the pointer to our head to inherited _headData variable from AvatarData - _headData = static_cast(new Head(this)); - - _skeletonModel = std::make_shared(this, nullptr, rig); - connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); - auto geometryCache = DependencyManager::get(); _nameRectGeometryID = geometryCache->allocateID(); _leftPointerGeometryID = geometryCache->allocateID(); @@ -283,7 +260,7 @@ void Avatar::updateAvatarEntities() { // and either add or update the entity. QJsonDocument jsonProperties = QJsonDocument::fromBinaryData(data); if (!jsonProperties.isObject()) { - qCDebug(interfaceapp) << "got bad avatarEntity json" << QString(data.toHex()); + qCDebug(avatars_renderer) << "got bad avatarEntity json" << QString(data.toHex()); continue; } @@ -306,7 +283,7 @@ void Avatar::updateAvatarEntities() { // NOTE: if this avatar entity is not attached to us, strip its entity script completely... auto attachedScript = properties.getScript(); if (!isMyAvatar() && !attachedScript.isEmpty()) { - qCDebug(interfaceapp) << "removing entity script from avatar attached entity:" << entityID << "old script:" << attachedScript; + qCDebug(avatars_renderer) << "removing entity script from avatar attached entity:" << entityID << "old script:" << attachedScript; QString noScript; properties.setScript(noScript); } @@ -410,7 +387,7 @@ void Avatar::simulate(float deltaTime, bool inView) { Head* head = getHead(); head->setPosition(headPosition); head->setScale(getUniformScale()); - head->simulate(deltaTime, false); + head->simulate(deltaTime); } else { // a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated. _skeletonModel->simulate(deltaTime, false); @@ -748,12 +725,12 @@ float Avatar::getBoundingRadius() const { #ifdef DEBUG void debugValue(const QString& str, const glm::vec3& value) { if (glm::any(glm::isnan(value)) || glm::any(glm::isinf(value))) { - qCWarning(interfaceapp) << "debugValue() " << str << value; + qCWarning(avatars_renderer) << "debugValue() " << str << value; } }; void debugValue(const QString& str, const float& value) { if (glm::isnan(value) || glm::isinf(value)) { - qCWarning(interfaceapp) << "debugValue() " << str << value; + qCWarning(avatars_renderer) << "debugValue() " << str << value; } }; #define DEBUG_VALUE(str, value) debugValue(str, value) @@ -783,7 +760,7 @@ glm::vec3 Avatar::getDisplayNamePosition() const { } if (glm::any(glm::isnan(namePosition)) || glm::any(glm::isinf(namePosition))) { - qCWarning(interfaceapp) << "Invalid display name position" << namePosition + qCWarning(avatars_renderer) << "Invalid display name position" << namePosition << ", setting is to (0.0f, 0.5f, 0.0f)"; namePosition = glm::vec3(0.0f, 0.5f, 0.0f); } @@ -1115,14 +1092,14 @@ void Avatar::setModelURLFinished(bool success) { const int MAX_SKELETON_DOWNLOAD_ATTEMPTS = 4; // NOTE: we don't want to be as generous as ResourceCache is, we only want 4 attempts if (_skeletonModel->getResourceDownloadAttemptsRemaining() <= 0 || _skeletonModel->getResourceDownloadAttempts() > MAX_SKELETON_DOWNLOAD_ATTEMPTS) { - qCWarning(interfaceapp) << "Using default after failing to load Avatar model: " << _skeletonModelURL + qCWarning(avatars_renderer) << "Using default after failing to load Avatar model: " << _skeletonModelURL << "after" << _skeletonModel->getResourceDownloadAttempts() << "attempts."; // call _skeletonModel.setURL, but leave our copy of _skeletonModelURL alone. This is so that // we don't redo this every time we receive an identity packet from the avatar with the bad url. QMetaObject::invokeMethod(_skeletonModel.get(), "setURL", Qt::QueuedConnection, Q_ARG(QUrl, AvatarData::defaultFullAvatarModelUrl())); } else { - qCWarning(interfaceapp) << "Avatar model: " << _skeletonModelURL + qCWarning(avatars_renderer) << "Avatar model: " << _skeletonModelURL << "failed to load... attempts:" << _skeletonModel->getResourceDownloadAttempts() << "out of:" << MAX_SKELETON_DOWNLOAD_ATTEMPTS; } @@ -1438,7 +1415,7 @@ void Avatar::setParentID(const QUuid& parentID) { if (success) { setTransform(beforeChangeTransform, success); if (!success) { - qCDebug(interfaceapp) << "Avatar::setParentID failed to reset avatar's location."; + qCDebug(avatars_renderer) << "Avatar::setParentID failed to reset avatar's location."; } if (initialParentID != parentID) { _parentChanged = usecTimestampNow(); @@ -1456,7 +1433,7 @@ void Avatar::setParentJointIndex(quint16 parentJointIndex) { if (success) { setTransform(beforeChangeTransform, success); if (!success) { - qCDebug(interfaceapp) << "Avatar::setParentJointIndex failed to reset avatar's location."; + qCDebug(avatars_renderer) << "Avatar::setParentJointIndex failed to reset avatar's location."; } } } @@ -1488,7 +1465,7 @@ QList Avatar::getSkeleton() { void Avatar::addToScene(AvatarSharedPointer myHandle, const render::ScenePointer& scene) { if (scene) { auto nodelist = DependencyManager::get(); - if (DependencyManager::get()->shouldRenderAvatars() + if (showAvatars && !nodelist->isIgnoringNode(getSessionUUID()) && !nodelist->isRadiusIgnoringNode(getSessionUUID())) { render::Transaction transaction; @@ -1496,7 +1473,7 @@ void Avatar::addToScene(AvatarSharedPointer myHandle, const render::ScenePointer scene->enqueueTransaction(transaction); } } else { - qCWarning(interfaceapp) << "Avatar::addAvatar() : Unexpected null scene, possibly during application shutdown"; + qCWarning(avatars_renderer) << "Avatar::addAvatar() : Unexpected null scene, possibly during application shutdown"; } } diff --git a/interface/src/avatar/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h similarity index 96% rename from interface/src/avatar/Avatar.h rename to libraries/avatars-renderer/src/avatars-renderer/Avatar.h index f86bf35bd9..20704a08b2 100644 --- a/interface/src/avatar/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "Head.h" @@ -68,6 +69,7 @@ class Avatar : public AvatarData { Q_PROPERTY(glm::vec3 skeletonOffset READ getSkeletonOffset WRITE setSkeletonOffset) public: + static void setShowAvatars(bool render); static void setShowReceiveStats(bool receiveStats); static void setShowMyLookAtVectors(bool showMine); static void setShowOtherLookAtVectors(bool showOthers); @@ -77,6 +79,8 @@ public: explicit Avatar(QThread* thread, RigPointer rig = nullptr); ~Avatar(); + virtual void instantiableAvatar() = 0; + typedef render::Payload Payload; typedef std::shared_ptr PayloadPointer; @@ -251,7 +255,6 @@ public: bool isInScene() const { return render::Item::isValidID(_renderItemID); } bool isMoving() const { return _moving; } - //void setMotionState(AvatarMotionState* motionState); void setPhysicsCallback(AvatarPhysicsCallback cb); void addPhysicsFlags(uint32_t flags); bool isInPhysicsSimulation() const { return _physicsCallback != nullptr; } @@ -268,7 +271,6 @@ public slots: void setModelURLFinished(bool success); protected: - const float SMOOTH_TIME_POSITION = 0.125f; const float SMOOTH_TIME_ORIENTATION = 0.075f; @@ -282,7 +284,7 @@ protected: std::vector> _attachmentsToRemove; std::vector> _attachmentsToDelete; - float _bodyYawDelta; // degrees/sec + float _bodyYawDelta { 0.0f }; // degrees/sec // These position histories and derivatives are in the world-frame. // The derivatives are the MEASURED results of all external and internal forces @@ -298,9 +300,8 @@ protected: glm::vec3 _angularAcceleration; glm::quat _lastOrientation; - glm::vec3 _worldUpDirection; - float _stringLength; - bool _moving; ///< set when position is changing + glm::vec3 _worldUpDirection { Vectors::UP }; + bool _moving { false }; ///< set when position is changing // protected methods... bool isLookingAtMe(AvatarSharedPointer avatar) const; @@ -336,10 +337,10 @@ protected: RateCounter<> _jointDataSimulationRate; // Smoothing data for blending from one position/orientation to another on remote agents. - float _smoothPositionTime; - float _smoothPositionTimer; - float _smoothOrientationTime; - float _smoothOrientationTimer; + float _smoothPositionTime { SMOOTH_TIME_POSITION }; + float _smoothPositionTimer { std::numeric_limits::max() }; + float _smoothOrientationTime { SMOOTH_TIME_ORIENTATION }; + float _smoothOrientationTimer { std::numeric_limits::max() }; glm::vec3 _smoothPositionInitial; glm::vec3 _smoothPositionTarget; glm::quat _smoothOrientationInitial; @@ -360,7 +361,7 @@ private: int _leftPointerGeometryID { 0 }; int _rightPointerGeometryID { 0 }; int _nameRectGeometryID { 0 }; - bool _initialized; + bool _initialized { false }; bool _isLookAtTarget { false }; bool _isAnimatingScale { false }; diff --git a/interface/src/avatar/AvatarMotionState.cpp b/libraries/avatars-renderer/src/avatars-renderer/AvatarMotionState.cpp similarity index 99% rename from interface/src/avatar/AvatarMotionState.cpp rename to libraries/avatars-renderer/src/avatars-renderer/AvatarMotionState.cpp index ffa99e3990..0305634400 100644 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/AvatarMotionState.cpp @@ -9,13 +9,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "AvatarMotionState.h" + #include #include #include -#include "Avatar.h" -#include "AvatarMotionState.h" -#include "BulletUtil.h" AvatarMotionState::AvatarMotionState(AvatarSharedPointer avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) { assert(_avatar); diff --git a/interface/src/avatar/AvatarMotionState.h b/libraries/avatars-renderer/src/avatars-renderer/AvatarMotionState.h similarity index 98% rename from interface/src/avatar/AvatarMotionState.h rename to libraries/avatars-renderer/src/avatars-renderer/AvatarMotionState.h index a8dd7327ca..f8801ddf2f 100644 --- a/interface/src/avatar/AvatarMotionState.h +++ b/libraries/avatars-renderer/src/avatars-renderer/AvatarMotionState.h @@ -15,8 +15,9 @@ #include #include +#include -class Avatar; +#include "Avatar.h" class AvatarMotionState : public ObjectMotionState { public: diff --git a/interface/src/avatar/Head.cpp b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp similarity index 77% rename from interface/src/avatar/Head.cpp rename to libraries/avatars-renderer/src/avatars-renderer/Head.cpp index 16e5776d87..a90c9cd5f7 100644 --- a/interface/src/avatar/Head.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Head.cpp @@ -8,55 +8,28 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "Head.h" + #include #include #include #include +#include +#include +#include #include - -#include "Application.h" -#include "Avatar.h" -#include "DependencyManager.h" -#include "GeometryUtil.h" -#include "Head.h" -#include "Menu.h" -#include "Util.h" -#include "devices/DdeFaceTracker.h" #include +#include "Avatar.h" + using namespace std; +static bool fixGaze { false }; +static bool disableEyelidAdjustment { false }; + Head::Head(Avatar* owningAvatar) : - HeadData((AvatarData*)owningAvatar), - _returnHeadToCenter(false), - _position(0.0f, 0.0f, 0.0f), - _rotation(0.0f, 0.0f, 0.0f), - _leftEyePosition(0.0f, 0.0f, 0.0f), - _rightEyePosition(0.0f, 0.0f, 0.0f), - _eyePosition(0.0f, 0.0f, 0.0f), - _scale(1.0f), - _lastLoudness(0.0f), - _longTermAverageLoudness(-1.0f), - _audioAttack(0.0f), - _audioJawOpen(0.0f), - _trailingAudioJawOpen(0.0f), - _mouth2(0.0f), - _mouth3(0.0f), - _mouth4(0.0f), - _mouthTime(0.0f), - _saccade(0.0f, 0.0f, 0.0f), - _saccadeTarget(0.0f, 0.0f, 0.0f), - _leftEyeBlinkVelocity(0.0f), - _rightEyeBlinkVelocity(0.0f), - _timeWithoutTalking(0.0f), - _deltaPitch(0.0f), - _deltaYaw(0.0f), - _deltaRoll(0.0f), - _isCameraMoving(false), - _isLookingAtMe(false), - _lookingAtMeStarted(0), - _wasLastLookingAtMe(0), + HeadData(owningAvatar), _leftEyeLookAtID(DependencyManager::get()->allocateID()), _rightEyeLookAtID(DependencyManager::get()->allocateID()) { @@ -69,7 +42,7 @@ void Head::reset() { _baseYaw = _basePitch = _baseRoll = 0.0f; } -void Head::simulate(float deltaTime, bool isMine) { +void Head::simulate(float deltaTime) { const float NORMAL_HZ = 60.0f; // the update rate the constant values were tuned for // grab the audio loudness from the owning avatar, if we have one @@ -90,43 +63,7 @@ void Head::simulate(float deltaTime, bool isMine) { _longTermAverageLoudness = glm::mix(_longTermAverageLoudness, _averageLoudness, glm::min(deltaTime / AUDIO_LONG_TERM_AVERAGING_SECS, 1.0f)); } - if (isMine) { - auto player = DependencyManager::get(); - // Only use face trackers when not playing back a recording. - if (!player->isPlaying()) { - FaceTracker* faceTracker = qApp->getActiveFaceTracker(); - _isFaceTrackerConnected = faceTracker != NULL && !faceTracker->isMuted(); - if (_isFaceTrackerConnected) { - _blendshapeCoefficients = faceTracker->getBlendshapeCoefficients(); - - if (typeid(*faceTracker) == typeid(DdeFaceTracker)) { - - if (Menu::getInstance()->isOptionChecked(MenuOption::UseAudioForMouth)) { - calculateMouthShapes(deltaTime); - - const int JAW_OPEN_BLENDSHAPE = 21; - const int MMMM_BLENDSHAPE = 34; - const int FUNNEL_BLENDSHAPE = 40; - const int SMILE_LEFT_BLENDSHAPE = 28; - const int SMILE_RIGHT_BLENDSHAPE = 29; - _blendshapeCoefficients[JAW_OPEN_BLENDSHAPE] += _audioJawOpen; - _blendshapeCoefficients[SMILE_LEFT_BLENDSHAPE] += _mouth4; - _blendshapeCoefficients[SMILE_RIGHT_BLENDSHAPE] += _mouth4; - _blendshapeCoefficients[MMMM_BLENDSHAPE] += _mouth2; - _blendshapeCoefficients[FUNNEL_BLENDSHAPE] += _mouth3; - } - - applyEyelidOffset(getFinalOrientationInWorldFrame()); - } - } - - auto eyeTracker = DependencyManager::get(); - _isEyeTrackerConnected = eyeTracker->isTracking(); - } - } - if (!_isFaceTrackerConnected) { - if (!_isEyeTrackerConnected) { // Update eye saccades const float AVERAGE_MICROSACCADE_INTERVAL = 1.0f; @@ -222,7 +159,7 @@ void Head::simulate(float deltaTime, bool isMine) { } else { _saccade = glm::vec3(); } - if (Menu::getInstance()->isOptionChecked(MenuOption::FixGaze)) { // if debug menu turns off, use no saccade + if (fixGaze) { // if debug menu turns off, use no saccade _saccade = glm::vec3(); } @@ -277,7 +214,7 @@ void Head::calculateMouthShapes(float deltaTime) { void Head::applyEyelidOffset(glm::quat headOrientation) { // Adjusts the eyelid blendshape coefficients so that the eyelid follows the iris as the head pitches. - if (Menu::getInstance()->isOptionChecked(MenuOption::DisableEyelidAdjustment)) { + if (disableEyelidAdjustment) { return; } @@ -350,7 +287,7 @@ glm::vec3 Head::getCorrectedLookAtPosition() { } } -void Head::setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition) { +void Head::setCorrectedLookAtPosition(const glm::vec3& correctedLookAtPosition) { if (!isLookingAtMe()) { _lookingAtMeStarted = usecTimestampNow(); } @@ -366,25 +303,6 @@ bool Head::isLookingAtMe() { return _isLookingAtMe || (now - _wasLastLookingAtMe) < LOOKING_AT_ME_GAP_ALLOWED; } -glm::quat Head::getCameraOrientation() const { - // NOTE: Head::getCameraOrientation() is not used for orienting the camera "view" while in Oculus mode, so - // you may wonder why this code is here. This method will be called while in Oculus mode to determine how - // to change the driving direction while in Oculus mode. It is used to support driving toward where you're - // head is looking. Note that in oculus mode, your actual camera view and where your head is looking is not - // always the same. - if (qApp->isHMDMode()) { - MyAvatar* myAvatar = dynamic_cast(_owningAvatar); - if (myAvatar) { - return glm::quat_cast(myAvatar->getSensorToWorldMatrix()) * myAvatar->getHMDSensorOrientation(); - } else { - return getOrientation(); - } - } else { - Avatar* owningAvatar = static_cast(_owningAvatar); - return owningAvatar->getWorldAlignedOrientation() * glm::quat(glm::radians(glm::vec3(_basePitch, 0.0f, 0.0f))); - } -} - glm::quat Head::getEyeRotation(const glm::vec3& eyePosition) const { glm::quat orientation = getOrientation(); glm::vec3 lookAtDelta = _lookAtPosition - eyePosition; diff --git a/interface/src/avatar/Head.h b/libraries/avatars-renderer/src/avatars-renderer/Head.h similarity index 79% rename from interface/src/avatar/Head.h rename to libraries/avatars-renderer/src/avatars-renderer/Head.h index fd20e709f5..aea6a41528 100644 --- a/interface/src/avatar/Head.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Head.h @@ -11,16 +11,10 @@ #ifndef hifi_Head_h #define hifi_Head_h -#include -#include - +#include #include - #include -#include "world.h" - - const float EYE_EAR_GAP = 0.08f; class Avatar; @@ -31,9 +25,9 @@ public: void init(); void reset(); - void simulate(float deltaTime, bool isMine); + virtual void simulate(float deltaTime); void setScale(float scale); - void setPosition(glm::vec3 position) { _position = position; } + void setPosition(const glm::vec3& position) { _position = position; } void setAverageLoudness(float averageLoudness) { _averageLoudness = averageLoudness; } void setReturnToCenter (bool returnHeadToCenter) { _returnHeadToCenter = returnHeadToCenter; } @@ -43,17 +37,14 @@ public: /// \return orientationBody * (orientationBase+Delta) glm::quat getFinalOrientationInWorldFrame() const; - /// \return orientationBody * orientationBasePitch - glm::quat getCameraOrientation () const; - - void setCorrectedLookAtPosition(glm::vec3 correctedLookAtPosition); + void setCorrectedLookAtPosition(const glm::vec3& correctedLookAtPosition); glm::vec3 getCorrectedLookAtPosition(); void clearCorrectedLookAtPosition() { _isLookingAtMe = false; } bool isLookingAtMe(); quint64 getLookingAtMeStarted() { return _lookingAtMeStarted; } float getScale() const { return _scale; } - glm::vec3 getPosition() const { return _position; } + const glm::vec3& getPosition() const { return _position; } const glm::vec3& getEyePosition() const { return _eyePosition; } const glm::vec3& getSaccade() const { return _saccade; } glm::vec3 getRightDirection() const { return getOrientation() * IDENTITY_RIGHT; } @@ -91,46 +82,46 @@ public: float getTimeWithoutTalking() const { return _timeWithoutTalking; } -private: +protected: glm::vec3 calculateAverageEyePosition() const { return _leftEyePosition + (_rightEyePosition - _leftEyePosition ) * 0.5f; } // disallow copies of the Head, copy of owning Avatar is disallowed too Head(const Head&); Head& operator= (const Head&); - bool _returnHeadToCenter; + bool _returnHeadToCenter { false }; glm::vec3 _position; glm::vec3 _rotation; glm::vec3 _leftEyePosition; glm::vec3 _rightEyePosition; glm::vec3 _eyePosition; - float _scale; - float _lastLoudness; - float _longTermAverageLoudness; - float _audioAttack; - float _audioJawOpen; - float _trailingAudioJawOpen; - float _mouth2; - float _mouth3; - float _mouth4; - float _mouthTime; + float _scale { 1.0f }; + float _lastLoudness { 0.0f }; + float _longTermAverageLoudness { -1.0f }; + float _audioAttack { 0.0f }; + float _audioJawOpen { 0.0f }; + float _trailingAudioJawOpen { 0.0f }; + float _mouth2 { 0.0f }; + float _mouth3 { 0.0f }; + float _mouth4 { 0.0f }; + float _mouthTime { 0.0f }; glm::vec3 _saccade; glm::vec3 _saccadeTarget; - float _leftEyeBlinkVelocity; - float _rightEyeBlinkVelocity; - float _timeWithoutTalking; + float _leftEyeBlinkVelocity { 0.0f }; + float _rightEyeBlinkVelocity { 0.0f }; + float _timeWithoutTalking { 0.0f }; // delta angles for local head rotation (driven by hardware input) - float _deltaPitch; - float _deltaYaw; - float _deltaRoll; + float _deltaPitch { 0.0f }; + float _deltaYaw { 0.0f }; + float _deltaRoll { 0.0f }; - bool _isCameraMoving; - bool _isLookingAtMe; - quint64 _lookingAtMeStarted; - quint64 _wasLastLookingAtMe; + bool _isCameraMoving { false }; + bool _isLookingAtMe { false }; + quint64 _lookingAtMeStarted { 0 }; + quint64 _wasLastLookingAtMe { 0 }; glm::vec3 _correctedLookAtPosition; diff --git a/libraries/avatars-renderer/src/AvatarsRendererLogging.cpp b/libraries/avatars-renderer/src/avatars-renderer/Logging.cpp similarity index 89% rename from libraries/avatars-renderer/src/AvatarsRendererLogging.cpp rename to libraries/avatars-renderer/src/avatars-renderer/Logging.cpp index 2804df1b7a..f50902354d 100644 --- a/libraries/avatars-renderer/src/AvatarsRendererLogging.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Logging.cpp @@ -6,6 +6,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "AvatarsRendererLogging.h" +#include "Logging.h" Q_LOGGING_CATEGORY(avatars_renderer, "hifi.avatars.rendering") diff --git a/libraries/avatars-renderer/src/AvatarsRendererLogging.h b/libraries/avatars-renderer/src/avatars-renderer/Logging.h similarity index 100% rename from libraries/avatars-renderer/src/AvatarsRendererLogging.h rename to libraries/avatars-renderer/src/avatars-renderer/Logging.h diff --git a/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.cpp new file mode 100644 index 0000000000..ad69ba24cb --- /dev/null +++ b/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.cpp @@ -0,0 +1,16 @@ +// +// Created by Bradley Austin Davis on 2017/04/27 +// Copyright 2013-2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "OtherAvatar.h" + +OtherAvatar::OtherAvatar(QThread* thread, RigPointer rig) : Avatar(thread, rig) { + // give the pointer to our head to inherited _headData variable from AvatarData + _headData = new Head(this); + _skeletonModel = std::make_shared(this, nullptr, rig); + connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); +} diff --git a/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.h b/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.h new file mode 100644 index 0000000000..2f6c9a38aa --- /dev/null +++ b/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.h @@ -0,0 +1,20 @@ +// +// Created by Bradley Austin Davis on 2017/04/27 +// Copyright 2013-2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_OtherAvatar_h +#define hifi_OtherAvatar_h + +#include "Avatar.h" + +class OtherAvatar : public Avatar { +public: + explicit OtherAvatar(QThread* thread, RigPointer rig = nullptr); + void instantiableAvatar() {}; +}; + +#endif // hifi_OtherAvatar_h diff --git a/interface/src/avatar/ScriptAvatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/ScriptAvatar.cpp similarity index 100% rename from interface/src/avatar/ScriptAvatar.cpp rename to libraries/avatars-renderer/src/avatars-renderer/ScriptAvatar.cpp diff --git a/interface/src/avatar/ScriptAvatar.h b/libraries/avatars-renderer/src/avatars-renderer/ScriptAvatar.h similarity index 100% rename from interface/src/avatar/ScriptAvatar.h rename to libraries/avatars-renderer/src/avatars-renderer/ScriptAvatar.h diff --git a/interface/src/avatar/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp similarity index 66% rename from interface/src/avatar/SkeletonModel.cpp rename to libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index f81a83523b..8fcc859b62 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -9,19 +9,18 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "SkeletonModel.h" + #include #include #include #include +#include +#include -#include "Application.h" #include "Avatar.h" -#include "Menu.h" -#include "SkeletonModel.h" -#include "Util.h" -#include "InterfaceLogging.h" -#include "AnimDebugDraw.h" +#include "Logging.h" SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) : CauterizedModel(rig, parent), @@ -47,7 +46,7 @@ void SkeletonModel::initJointStates() { // Determine the default eye position for avatar scale = 1.0 int headJointIndex = geometry.headJointIndex; if (0 > headJointIndex || headJointIndex >= _rig->getJointStateCount()) { - qCWarning(interfaceapp) << "Bad head joint! Got:" << headJointIndex << "jointCount:" << _rig->getJointStateCount(); + qCWarning(avatars_renderer) << "Bad head joint! Got:" << headJointIndex << "jointCount:" << _rig->getJointStateCount(); } glm::vec3 leftEyePosition, rightEyePosition; getEyeModelPositions(leftEyePosition, rightEyePosition); @@ -72,21 +71,6 @@ void SkeletonModel::initJointStates() { emit skeletonLoaded(); } -Rig::CharacterControllerState convertCharacterControllerState(CharacterController::State state) { - switch (state) { - default: - case CharacterController::State::Ground: - return Rig::CharacterControllerState::Ground; - case CharacterController::State::Takeoff: - return Rig::CharacterControllerState::Takeoff; - case CharacterController::State::InAir: - return Rig::CharacterControllerState::InAir; - case CharacterController::State::Hover: - return Rig::CharacterControllerState::Hover; - }; -} - - // Called within Model::simulate call, below. void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { const FBXGeometry& geometry = getFBXGeometry(); @@ -102,122 +86,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { lookAt = _owningAvatar->getHead()->getEyePosition() + (MIN_LOOK_AT_FOCUS_DISTANCE / focusDistance) * focusOffset; } - if (_owningAvatar->isMyAvatar()) { - MyAvatar* myAvatar = static_cast(_owningAvatar); - - Rig::HeadParameters headParams; - - // input action is the highest priority source for head orientation. - auto avatarHeadPose = myAvatar->getHeadControllerPoseInAvatarFrame(); - if (avatarHeadPose.isValid()) { - glm::mat4 rigHeadMat = Matrices::Y_180 * createMatFromQuatAndPos(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation()); - headParams.rigHeadPosition = extractTranslation(rigHeadMat); - headParams.rigHeadOrientation = glmExtractRotation(rigHeadMat); - headParams.headEnabled = true; - } else { - if (qApp->isHMDMode()) { - // get HMD position from sensor space into world space, and back into rig space - glm::mat4 worldHMDMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); - glm::mat4 rigToWorld = createMatFromQuatAndPos(getRotation(), getTranslation()); - glm::mat4 worldToRig = glm::inverse(rigToWorld); - glm::mat4 rigHMDMat = worldToRig * worldHMDMat; - _rig->computeHeadFromHMD(AnimPose(rigHMDMat), headParams.rigHeadPosition, headParams.rigHeadOrientation); - headParams.headEnabled = true; - } else { - // even though full head IK is disabled, the rig still needs the head orientation to rotate the head up and down in desktop mode. - // preMult 180 is necessary to convert from avatar to rig coordinates. - // postMult 180 is necessary to convert head from -z forward to z forward. - headParams.rigHeadOrientation = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame() * Quaternions::Y_180; - headParams.headEnabled = false; - } - } - - auto avatarHipsPose = myAvatar->getHipsControllerPoseInAvatarFrame(); - if (avatarHipsPose.isValid()) { - glm::mat4 rigHipsMat = Matrices::Y_180 * createMatFromQuatAndPos(avatarHipsPose.getRotation(), avatarHipsPose.getTranslation()); - headParams.hipsMatrix = rigHipsMat; - headParams.hipsEnabled = true; - } else { - headParams.hipsEnabled = false; - } - - auto avatarSpine2Pose = myAvatar->getSpine2ControllerPoseInAvatarFrame(); - if (avatarSpine2Pose.isValid()) { - glm::mat4 rigSpine2Mat = Matrices::Y_180 * createMatFromQuatAndPos(avatarSpine2Pose.getRotation(), avatarSpine2Pose.getTranslation()); - headParams.spine2Matrix = rigSpine2Mat; - headParams.spine2Enabled = true; - } else { - headParams.spine2Enabled = false; - } - - headParams.isTalking = head->getTimeWithoutTalking() <= 1.5f; - - _rig->updateFromHeadParameters(headParams, deltaTime); - - Rig::HandAndFeetParameters handAndFeetParams; - - auto leftPose = myAvatar->getLeftHandControllerPoseInAvatarFrame(); - if (leftPose.isValid()) { - handAndFeetParams.isLeftEnabled = true; - handAndFeetParams.leftPosition = Quaternions::Y_180 * leftPose.getTranslation(); - handAndFeetParams.leftOrientation = Quaternions::Y_180 * leftPose.getRotation(); - } else { - handAndFeetParams.isLeftEnabled = false; - } - - auto rightPose = myAvatar->getRightHandControllerPoseInAvatarFrame(); - if (rightPose.isValid()) { - handAndFeetParams.isRightEnabled = true; - handAndFeetParams.rightPosition = Quaternions::Y_180 * rightPose.getTranslation(); - handAndFeetParams.rightOrientation = Quaternions::Y_180 * rightPose.getRotation(); - } else { - handAndFeetParams.isRightEnabled = false; - } - - auto leftFootPose = myAvatar->getLeftFootControllerPoseInAvatarFrame(); - if (leftFootPose.isValid()) { - handAndFeetParams.isLeftFootEnabled = true; - handAndFeetParams.leftFootPosition = Quaternions::Y_180 * leftFootPose.getTranslation(); - handAndFeetParams.leftFootOrientation = Quaternions::Y_180 * leftFootPose.getRotation(); - } else { - handAndFeetParams.isLeftFootEnabled = false; - } - - auto rightFootPose = myAvatar->getRightFootControllerPoseInAvatarFrame(); - if (rightFootPose.isValid()) { - handAndFeetParams.isRightFootEnabled = true; - handAndFeetParams.rightFootPosition = Quaternions::Y_180 * rightFootPose.getTranslation(); - handAndFeetParams.rightFootOrientation = Quaternions::Y_180 * rightFootPose.getRotation(); - } else { - handAndFeetParams.isRightFootEnabled = false; - } - - handAndFeetParams.bodyCapsuleRadius = myAvatar->getCharacterController()->getCapsuleRadius(); - handAndFeetParams.bodyCapsuleHalfHeight = myAvatar->getCharacterController()->getCapsuleHalfHeight(); - handAndFeetParams.bodyCapsuleLocalOffset = myAvatar->getCharacterController()->getCapsuleLocalOffset(); - - _rig->updateFromHandAndFeetParameters(handAndFeetParams, deltaTime); - - Rig::CharacterControllerState ccState = convertCharacterControllerState(myAvatar->getCharacterController()->getState()); - - auto velocity = myAvatar->getLocalVelocity(); - auto position = myAvatar->getLocalPosition(); - auto orientation = myAvatar->getLocalOrientation(); - _rig->computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState); - - // evaluate AnimGraph animation and update jointStates. - Model::updateRig(deltaTime, parentTransform); - - Rig::EyeParameters eyeParams; - eyeParams.eyeLookAt = lookAt; - eyeParams.eyeSaccade = head->getSaccade(); - eyeParams.modelRotation = getRotation(); - eyeParams.modelTranslation = getTranslation(); - eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex; - eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex; - - _rig->updateFromEyeParameters(eyeParams); - } else { + if (!_owningAvatar->isMyAvatar()) { // no need to call Model::updateRig() because otherAvatars get their joint state // copied directly from AvtarData::_jointData (there are no Rig animations to blend) _needsUpdateClusterMatrices = true; @@ -249,6 +118,9 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig->updateFromEyeParameters(eyeParams); } + + // evaluate AnimGraph animation and update jointStates. + Model::updateRig(deltaTime, parentTransform); } void SkeletonModel::updateAttitude() { diff --git a/interface/src/avatar/SkeletonModel.h b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h similarity index 99% rename from interface/src/avatar/SkeletonModel.h rename to libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h index 7a6081a010..23af04e964 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h @@ -114,7 +114,7 @@ protected: void computeBoundingShape(); -private: +protected: bool getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index b4c79470de..6196c4eeb3 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -94,7 +94,7 @@ const quint32 AVATAR_MOTION_SCRIPTABLE_BITS = // +-----+-----+-+-+-+--+ // Key state - K0,K1 is found in the 1st and 2nd bits // Hand state - H0,H1,H2 is found in the 3rd, 4th, and 8th bits -// Faceshift - F is found in the 5th bit +// Face tracker - F is found in the 5th bit // Eye tracker - E is found in the 6th bit // Referential Data - R is found in the 7th bit const int KEY_STATE_START_BIT = 0; // 1st and 2nd bits @@ -123,7 +123,7 @@ namespace AvatarDataPacket { // it might be nice to use a dictionary to compress that // Packet State Flags - we store the details about the existence of other records in this bitset: - // AvatarGlobalPosition, Avatar Faceshift, eye tracking, and existence of + // AvatarGlobalPosition, Avatar face tracker, eye tracking, and existence of using HasFlags = uint16_t; const HasFlags PACKET_HAS_AVATAR_GLOBAL_POSITION = 1U << 0; const HasFlags PACKET_HAS_AVATAR_BOUNDING_BOX = 1U << 1; diff --git a/libraries/avatars/src/HeadData.cpp b/libraries/avatars/src/HeadData.cpp index 271ce133a2..b55be7c156 100644 --- a/libraries/avatars/src/HeadData.cpp +++ b/libraries/avatars/src/HeadData.cpp @@ -23,11 +23,6 @@ #include "AvatarData.h" -/// The names of the blendshapes expected by Faceshift, terminated with an empty string. -extern const char* FACESHIFT_BLENDSHAPES[]; -/// The size of FACESHIFT_BLENDSHAPES -extern const int NUM_FACESHIFT_BLENDSHAPES; - HeadData::HeadData(AvatarData* owningAvatar) : _baseYaw(0.0f), _basePitch(0.0f), diff --git a/interface/src/avatar/CauterizedMeshPartPayload.cpp b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp similarity index 94% rename from interface/src/avatar/CauterizedMeshPartPayload.cpp rename to libraries/render-utils/src/CauterizedMeshPartPayload.cpp index c11f92083b..2e3d0385cd 100644 --- a/interface/src/avatar/CauterizedMeshPartPayload.cpp +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp @@ -13,7 +13,7 @@ #include -#include "SkeletonModel.h" +#include "CauterizedModel.h" using namespace render; @@ -29,7 +29,7 @@ void CauterizedMeshPartPayload::updateTransformForCauterizedMesh( void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { // Still relying on the raw data from the model - SkeletonModel* skeleton = static_cast(_model); + CauterizedModel* skeleton = static_cast(_model); bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE) && skeleton->getEnableCauterization(); if (useCauterizedMesh) { diff --git a/interface/src/avatar/CauterizedMeshPartPayload.h b/libraries/render-utils/src/CauterizedMeshPartPayload.h similarity index 87% rename from interface/src/avatar/CauterizedMeshPartPayload.h rename to libraries/render-utils/src/CauterizedMeshPartPayload.h index dc88e950c1..010cd6fcb6 100644 --- a/interface/src/avatar/CauterizedMeshPartPayload.h +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.h @@ -1,9 +1,6 @@ // -// CauterizedModelMeshPartPayload.h -// interface/src/avatar -// // Created by AndrewMeadows 2017.01.17 -// Copyright 2017 High Fidelity, Inc. +// Copyright 2013-2017 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -12,7 +9,7 @@ #ifndef hifi_CauterizedMeshPartPayload_h #define hifi_CauterizedMeshPartPayload_h -#include +#include "MeshPartPayload.h" class CauterizedMeshPartPayload : public ModelMeshPartPayload { public: diff --git a/interface/src/avatar/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp similarity index 98% rename from interface/src/avatar/CauterizedModel.cpp rename to libraries/render-utils/src/CauterizedModel.cpp index d7b7e0fedf..14625952ea 100644 --- a/interface/src/avatar/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -1,7 +1,4 @@ // -// CauterizedModel.cpp -// interface/src/avatar -// // Created by Andrew Meadows 2017.01.17 // Copyright 2017 High Fidelity, Inc. // @@ -11,10 +8,10 @@ #include "CauterizedModel.h" -#include -#include #include +#include "AbstractViewStateInterface.h" +#include "MeshPartPayload.h" #include "CauterizedMeshPartPayload.h" #include "RenderUtilsLogging.h" diff --git a/interface/src/avatar/CauterizedModel.h b/libraries/render-utils/src/CauterizedModel.h similarity index 95% rename from interface/src/avatar/CauterizedModel.h rename to libraries/render-utils/src/CauterizedModel.h index ba12aee32b..39bcedf639 100644 --- a/interface/src/avatar/CauterizedModel.h +++ b/libraries/render-utils/src/CauterizedModel.h @@ -1,7 +1,4 @@ // -// CauterizeableModel.h -// interface/src/avatar -// // Created by Andrew Meadows 2016.01.17 // Copyright 2017 High Fidelity, Inc. // @@ -13,7 +10,7 @@ #define hifi_CauterizedModel_h -#include +#include "Model.h" class CauterizedModel : public Model { Q_OBJECT diff --git a/interface/src/avatar/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp similarity index 97% rename from interface/src/avatar/SoftAttachmentModel.cpp rename to libraries/render-utils/src/SoftAttachmentModel.cpp index 0521f7a893..8fef0f8f77 100644 --- a/interface/src/avatar/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -1,7 +1,4 @@ // -// SoftAttachmentModel.cpp -// interface/src/avatar -// // Created by Anthony J. Thibault on 12/17/15. // Copyright 2013 High Fidelity, Inc. // @@ -10,7 +7,6 @@ // #include "SoftAttachmentModel.h" -#include "InterfaceLogging.h" SoftAttachmentModel::SoftAttachmentModel(RigPointer rig, QObject* parent, RigPointer rigOverride) : CauterizedModel(rig, parent), diff --git a/interface/src/avatar/SoftAttachmentModel.h b/libraries/render-utils/src/SoftAttachmentModel.h similarity index 95% rename from interface/src/avatar/SoftAttachmentModel.h rename to libraries/render-utils/src/SoftAttachmentModel.h index fea679839a..b66c1a289a 100644 --- a/interface/src/avatar/SoftAttachmentModel.h +++ b/libraries/render-utils/src/SoftAttachmentModel.h @@ -1,7 +1,4 @@ // -// SoftAttachmentModel.h -// interface/src/avatar -// // Created by Anthony J. Thibault on 12/17/15. // Copyright 2015 High Fidelity, Inc. // diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index db42fef8bc..70237e8ff6 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -582,3 +582,9 @@ glm::mat4 orthoInverse(const glm::mat4& m) { r[3][3] = 1.0f; return r; } + +// Return a random vector of average length 1 +glm::vec3 randVector() { + return glm::vec3(randFloat() - 0.5f, randFloat() - 0.5f, randFloat() - 0.5f) * 2.0f; +} + diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index fd75fa416c..ef92552d1f 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -252,6 +252,9 @@ inline bool isNaN(const glm::quat& value) { return isNaN(value.w) || isNaN(value glm::mat4 orthoInverse(const glm::mat4& m); +// Return a random vector of average length 1 +glm::vec3 randVector(); + // // Safe replacement of glm_mat4_mul() for unaligned arguments instead of __m128 // diff --git a/interface/src/Camera.cpp b/libraries/shared/src/shared/Camera.cpp similarity index 79% rename from interface/src/Camera.cpp rename to libraries/shared/src/shared/Camera.cpp index 37f53e3cc2..48fea9e835 100644 --- a/interface/src/Camera.cpp +++ b/libraries/shared/src/shared/Camera.cpp @@ -8,16 +8,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include - -#include -#include - -#include "Application.h" #include "Camera.h" -#include "Menu.h" -#include "Util.h" - CameraMode stringToMode(const QString& mode) { if (mode == "third person") { @@ -102,35 +93,9 @@ void Camera::setProjection(const glm::mat4& projection) { _projection = projection; } -PickRay Camera::computePickRay(float x, float y) { - return qApp->computePickRay(x, y); -} - void Camera::setModeString(const QString& mode) { CameraMode targetMode = stringToMode(mode); - switch (targetMode) { - case CAMERA_MODE_FIRST_PERSON: - Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, true); - break; - case CAMERA_MODE_THIRD_PERSON: - Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true); - break; - case CAMERA_MODE_MIRROR: - Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, true); - break; - case CAMERA_MODE_INDEPENDENT: - Menu::getInstance()->setIsOptionChecked(MenuOption::IndependentMode, true); - break; - case CAMERA_MODE_ENTITY: - Menu::getInstance()->setIsOptionChecked(MenuOption::CameraEntityMode, true); - break; - default: - break; - } - - qApp->cameraMenuChanged(); - if (_mode != targetMode) { setMode(targetMode); } diff --git a/interface/src/Camera.h b/libraries/shared/src/shared/Camera.h similarity index 94% rename from interface/src/Camera.h rename to libraries/shared/src/shared/Camera.h index c5a3b192ba..5f2162ff6e 100644 --- a/interface/src/Camera.h +++ b/libraries/shared/src/shared/Camera.h @@ -11,11 +11,9 @@ #ifndef hifi_Camera_h #define hifi_Camera_h -#include -#include -#include -#include -#include +#include "../GLMHelpers.h" +#include "../RegisteredMetaTypes.h" +#include "../ViewFrustum.h" enum CameraMode { @@ -87,7 +85,7 @@ public slots: * @param {float} y Y-coordinate on screen. * @return {PickRay} The computed {PickRay}. */ - PickRay computePickRay(float x, float y); + virtual PickRay computePickRay(float x, float y) const = 0; /**jsdoc * Set the camera to look at position position. Only works while in independent. diff --git a/tests/render-perf/src/Camera.hpp b/tests/render-perf/src/Camera.hpp index ada1277c47..0fea933041 100644 --- a/tests/render-perf/src/Camera.hpp +++ b/tests/render-perf/src/Camera.hpp @@ -2,7 +2,7 @@ #include -class Camera { +class SimpleCamera { protected: float fov { 60.0f }; float znear { DEFAULT_NEAR_CLIP }, zfar { DEFAULT_FAR_CLIP }; @@ -42,7 +42,7 @@ public: std::bitset keys; - Camera() { + SimpleCamera() { matrices.perspective = glm::perspective(glm::radians(fov), aspect, znear, zfar); } diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index a1120ee3e1..cff866edbf 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -109,7 +109,7 @@ public: } }; -class QWindowCamera : public Camera { +class QWindowCamera : public SimpleCamera { Key forKey(int key) { switch (key) { case Qt::Key_W: return FORWARD; @@ -1067,7 +1067,7 @@ private: } void cycleMode() { - static auto defaultProjection = Camera().matrices.perspective; + static auto defaultProjection = SimpleCamera().matrices.perspective; _renderMode = (RenderMode)((_renderMode + 1) % RENDER_MODE_COUNT); if (_renderMode == HMD) { _camera.matrices.perspective[0] = vec4 { 0.759056330, 0.000000000, 0.000000000, 0.000000000 }; From 9954380ce33d36bf79ba2af8f8df02ae681b3073 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 4 May 2017 14:44:48 -0700 Subject: [PATCH 21/26] don't filter out the place you're at anymore --- interface/resources/qml/hifi/Feed.qml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/hifi/Feed.qml b/interface/resources/qml/hifi/Feed.qml index fd3472b7be..fc108f47e3 100644 --- a/interface/resources/qml/hifi/Feed.qml +++ b/interface/resources/qml/hifi/Feed.qml @@ -156,10 +156,8 @@ Column { function makeFilteredStoryProcessor() { // answer a function(storyData) that adds it to suggestions if it matches var words = filter.toUpperCase().split(/\s+/).filter(identity); function suggestable(story) { - if (story.action === 'snapshot') { - return true; - } - return story.place_name !== AddressManager.placename; // Not our entry, but do show other entry points to current domain. + // We could filter out places we don't want to suggest, such as those where (story.place_name === AddressManager.placename) or (story.username === Account.username). + return true; } function matches(story) { if (!words.length) { From 25ab8e7e9443632253e5a69b392998025a88e09f Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Thu, 4 May 2017 14:45:58 -0700 Subject: [PATCH 22/26] debugging on call, but mostly, specify json headers, etc. --- scripts/system/tablet-goto.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/scripts/system/tablet-goto.js b/scripts/system/tablet-goto.js index 2cdfecede4..fb842d1314 100644 --- a/scripts/system/tablet-goto.js +++ b/scripts/system/tablet-goto.js @@ -21,7 +21,7 @@ if (!DEBUG) { return; } - print([].map.call(arguments, JSON.stringify)); + print('tablet-goto.js:', [].map.call(arguments, JSON.stringify)); } var gotoQmlSource = "TabletAddressDialog.qml"; @@ -46,6 +46,7 @@ switch (message.method) { case 'request': request(message.params, function (error, data) { + debug('rpc', request, 'error:', error, 'data:', data); response.error = error; response.result = data; tablet.sendToQml(response); @@ -109,12 +110,14 @@ var stories = {}, pingPong = false; function expire(id) { - request({ + var options = { uri: location.metaverseServerUrl + '/api/v1/user_stories/' + id, method: 'PUT', - body: {expired: true} - }, function (error, response) { - debug('expired story', id, 'error:', error, 'response:', response); + json: true, + body: {expire: "true"} + }; + request(options, function (error, response) { + debug('expired story', options, 'error:', error, 'response:', response); if (error || (response.status !== 'success')) { print("ERROR expiring story: ", error || response.status); } @@ -147,7 +150,9 @@ var stored = stories[story.id], storedOrNew = stored || story; debug('story exists:', !!stored, storedOrNew); if ((storedOrNew.username === Account.username) && (storedOrNew.place_name !== location.placename)) { - expire(story.id); + if (storedOrNew.audience == 'for_connections') { // Only expire if we haven't already done so. + expire(story.id); + } return; // before marking } storedOrNew.pingPong = pingPong; @@ -161,6 +166,7 @@ }); for (key in stories) { // Any story we were tracking that was not marked, has expired. if (stories[key].pingPong !== pingPong) { + debug('removing story', key); delete stories[key]; } } From 43a177cf9a3c3d9e271d1d7c7cac4ee3023384d3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 4 May 2017 15:22:44 -0700 Subject: [PATCH 23/26] CR feedback --- libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 98e4299a13..be55653f64 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -502,7 +502,10 @@ static TextRenderer3D* textRenderer(TextRendererType type) { void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) { auto avatarPayload = new render::Payload(self); auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload); - _renderItemID = scene->allocateID(); + + if (_renderItemID == render::Item::INVALID_ITEM_ID) { + _renderItemID = scene->allocateID(); + } transaction.resetItem(_renderItemID, avatarPayloadPointer); _skeletonModel->addToScene(scene, transaction); for (auto& attachmentModel : _attachmentModels) { From 946c5e766253e1da45a8dfc3f5a958a79467e609 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 4 May 2017 15:24:51 -0700 Subject: [PATCH 24/26] Almost finished with not-logged-in and not-shareable --- scripts/system/html/css/SnapshotReview.css | 14 ++ scripts/system/html/css/hifi-style.css | 10 +- scripts/system/html/js/SnapshotReview.js | 184 ++++++++++++++++----- scripts/system/snapshot.js | 147 +++++++++------- 4 files changed, 248 insertions(+), 107 deletions(-) diff --git a/scripts/system/html/css/SnapshotReview.css b/scripts/system/html/css/SnapshotReview.css index bd9bb81fdc..69de4bb147 100644 --- a/scripts/system/html/css/SnapshotReview.css +++ b/scripts/system/html/css/SnapshotReview.css @@ -187,6 +187,20 @@ input[type=button].naked:active { padding-left: 8px; color: white; } +.helpTextDiv { + width: 350px; + height: 65px; + margin-right: 15px; + line-height: 65px; + position: absolute; + bottom: 0; + right: 0; + font-family: Raleway-Regular; + font-weight: 500; + font-size: 16px; + text-align: right; + color: white; +} /* // END styling of share overlay */ diff --git a/scripts/system/html/css/hifi-style.css b/scripts/system/html/css/hifi-style.css index ac34cee09f..ec6cd1a402 100644 --- a/scripts/system/html/css/hifi-style.css +++ b/scripts/system/html/css/hifi-style.css @@ -136,11 +136,14 @@ input[type=radio]:active + label > span > span{ } .grayButton { - font-family: FiraSans-SemiBold; - color: white; + font-family: Raleway-Bold; + font-size: 13px; + color: black; padding: 0px 10px; + border-radius: 3px; border-width: 0px; background-image: linear-gradient(#FFFFFF, #AFAFAF); + min-height: 30px; } .grayButton:hover { background-image: linear-gradient(#FFFFFF, #FFFFFF); @@ -152,7 +155,8 @@ input[type=radio]:active + label > span > span{ background-image: linear-gradient(#FFFFFF, ##AFAFAF); } .blueButton { - font-family: FiraSans-SemiBold; + font-family: Raleway-Bold; + font-size: 13px; color: white; padding: 0px 10px; border-radius: 3px; diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index 27062faea2..dc0d319d16 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -62,6 +62,12 @@ function chooseSnapshotLocation() { action: "chooseSnapshotLocation" })); } +function login() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "snapshot", + action: "login" + })); +} function clearImages() { document.getElementById("snap-button").disabled = false; var snapshotImagesDiv = document.getElementById("snapshot-images"); @@ -74,6 +80,52 @@ function clearImages() { idCounter = 0; } +function selectImageWithHelpText(selectedID, isSelected) { + if (selectedID.id) { + selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID + } + var imageContainer = document.getElementById(selectedID), + image = document.getElementById(selectedID + 'img'), + shareBar = document.getElementById(selectedID + "shareBar"), + helpTextDiv = document.getElementById(selectedID + "helpTextDiv"), + showShareButtonsButtonDiv = document.getElementById(selectedID + "showShareButtonsButtonDiv"), + itr, + containers = document.getElementsByClassName("shareControls"); + + if (isSelected) { + showShareButtonsButtonDiv.onclick = function () { selectImageWithHelpText(selectedID, false); }; + showShareButtonsButtonDiv.classList.remove("inactive"); + showShareButtonsButtonDiv.classList.add("active"); + + image.onclick = function () { selectImageWithHelpText(selectedID, false); }; + imageContainer.style.outline = "4px solid #00b4ef"; + imageContainer.style.outlineOffset = "-4px"; + + shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.45)"; + shareBar.style.pointerEvents = "initial"; + + helpTextDiv.style.visibility = "visible"; + + for (itr = 0; itr < containers.length; itr += 1) { + var parentID = containers[itr].id.slice(0, 2); + if (parentID !== selectedID) { + selectImageWithHelpText(parentID, false); + } + } + } else { + showShareButtonsButtonDiv.onclick = function () { selectImageWithHelpText(selectedID, true); }; + showShareButtonsButtonDiv.classList.remove("active"); + showShareButtonsButtonDiv.classList.add("inactive"); + + image.onclick = function () { selectImageWithHelpText(selectedID, true); }; + imageContainer.style.outline = "none"; + + shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.0)"; + shareBar.style.pointerEvents = "none"; + + helpTextDiv.style.visibility = "hidden"; + } +} function selectImageToShare(selectedID, isSelected) { if (selectedID.id) { selectedID = selectedID.id; // sometimes (?), `selectedID` is passed as an HTML object to these functions; we just want the ID @@ -97,6 +149,7 @@ function selectImageToShare(selectedID, isSelected) { imageContainer.style.outlineOffset = "-4px"; shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.45)"; + shareBar.style.pointerEvents = "initial"; shareButtonsDiv.style.visibility = "visible"; shareBarHelp.style.visibility = "visible"; @@ -116,12 +169,13 @@ function selectImageToShare(selectedID, isSelected) { imageContainer.style.outline = "none"; shareBar.style.backgroundColor = "rgba(0, 0, 0, 0.0)"; + shareBar.style.pointerEvents = "none"; shareButtonsDiv.style.visibility = "hidden"; shareBarHelp.style.visibility = "hidden"; } } -function createShareBar(parentID, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast) { +function createShareBar(parentID, isLoggedIn, canShare, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast) { var shareBar = document.createElement("div"), shareBarHelpID = parentID + "shareBarHelp", shareButtonsDivID = parentID + "shareButtonsDiv", @@ -130,45 +184,85 @@ function createShareBar(parentID, isGif, blastButtonDisabled, hifiButtonDisabled blastToConnectionsButtonID = parentID + "blastToConnectionsButton", shareWithEveryoneButtonID = parentID + "shareWithEveryoneButton", facebookButtonID = parentID + "facebookButton", - twitterButtonID = parentID + "twitterButton"; + twitterButtonID = parentID + "twitterButton", + shareBarInnerHTML = ''; shareBar.id = parentID + "shareBar"; shareBar.className = "shareControls"; - var shareBarInnerHTML = '' + - '
' + - '' + - '' + - '' + + + if (isLoggedIn) { + if (canShare) { + shareBarInnerHTML = '' + + '
' + + '' + + '' + + '' + + '
' + + '
' + + ''; + + // Add onclick handler to parent DIV's img to toggle share buttons + document.getElementById(parentID + 'img').onclick = function () { selectImageToShare(parentID, true); }; + } else { + shareBarInnerHTML = '
' + + '' + + '' + + '' + + '
' + + '
' + + ''; + // Add onclick handler to parent DIV's img to toggle share buttons + document.getElementById(parentID + 'img').onclick = function () { selectImageWithHelpText(parentID, true); }; + } + } else { + shareBarInnerHTML = '
' + + '' + + '' + + '' + + '
' + '' + - '' + - ''; shareBar.innerHTML = shareBarInnerHTML; - // Add onclick handler to parent DIV's img to toggle share buttons - document.getElementById(parentID + 'img').onclick = function () { selectImageToShare(parentID, true); }; - return shareBar; } -function appendShareBar(divID, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast) { +function appendShareBar(divID, isLoggedIn, canShare, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast) { if (divID.id) { divID = divID.id; // sometimes (?), `containerID` is passed as an HTML object to these functions; we just want the ID } - document.getElementById(divID).appendChild(createShareBar(divID, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast)); + document.getElementById(divID).appendChild(createShareBar(divID, isLoggedIn, canShare, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast)); if (divID === "p0") { - selectImageToShare(divID, true); + if (isLoggedIn) { + if (canShare) { + selectImageWithHelpText(divID, true); + } else { + selectImageToShare(divID, true); + } + } else { + selectImageWithHelpText(divID, true); + } } - if (canBlast) { - shareButtonHovered('blast', divID, false); - } else { - shareButtonHovered('hifi', divID, false); + if (isLoggedIn && canShare) { + if (canBlast) { + shareButtonHovered('blast', divID, false); + } else { + shareButtonHovered('hifi', divID, false); + } } } function shareForUrl(selectedID) { @@ -178,7 +272,7 @@ function shareForUrl(selectedID) { data: paths[parseInt(selectedID.substring(1), 10)] })); } -function addImage(image_data, isGifLoading, canShare, isShowingPreviousImages, blastButtonDisabled, hifiButtonDisabled, canBlast) { +function addImage(image_data, isLoggedIn, canShare, isGifLoading, isShowingPreviousImages, blastButtonDisabled, hifiButtonDisabled, canBlast) { if (!image_data.localPath) { return; } @@ -203,12 +297,13 @@ function addImage(image_data, isGifLoading, canShare, isShowingPreviousImages, b if (isGif) { imageContainer.innerHTML += 'GIF'; } - if (!isGifLoading && !isShowingPreviousImages && canShare) { - appendShareBar(id, isGif, blastButtonDisabled, hifiButtonDisabled, true); + if (!isGifLoading) { + appendShareBar(id, isLoggedIn, canShare, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast); + } + if (!isGifLoading && !isShowingPreviousImages) { shareForUrl(id); } - if (isShowingPreviousImages && image_data.story_id) { - appendShareBar(id, isGif, blastButtonDisabled, hifiButtonDisabled, canBlast); + if (isShowingPreviousImages && isLoggedIn && image_data.story_id) { updateShareInfo(id, image_data.story_id); } } @@ -485,7 +580,7 @@ function handleCaptureSetting(setting) { window.onload = function () { // Uncomment the line below to test functionality in a browser. // See definition of "testInBrowser()" to modify tests. - //testInBrowser(3); + //testInBrowser(4); openEventBridge(function () { // Set up a handler for receiving the data, and tell the .js we are ready to receive it. EventBridge.scriptEventReceived.connect(function (message) { @@ -514,7 +609,7 @@ window.onload = function () { imageCount = message.image_data.length; if (imageCount > 0) { message.image_data.forEach(function (element, idx) { - addImage(element, true, message.canShare, true, message.image_data[idx].blastButtonDisabled, message.image_data[idx].hifiButtonDisabled, messageOptions.canBlast); + addImage(element, messageOptions.isLoggedIn, message.canShare, false, true, message.image_data[idx].blastButtonDisabled, message.image_data[idx].hifiButtonDisabled, messageOptions.canBlast); }); } else { showSnapshotInstructions(); @@ -530,7 +625,7 @@ window.onload = function () { imageCount = message.image_data.length + 1; // "+1" for the GIF that'll finish processing soon message.image_data.push({ localPath: messageOptions.loadingGifPath }); message.image_data.forEach(function (element, idx) { - addImage(element, idx === 1, idx === 0 && messageOptions.canShare, false); + addImage(element, messageOptions.isLoggedIn, idx === 0 && messageOptions.canShare, idx === 1, false); }); document.getElementById("p1").classList.add("processingGif"); } else { @@ -541,14 +636,14 @@ window.onload = function () { paths[1] = gifPath; if (messageOptions.canShare) { shareForUrl("p1"); - appendShareBar("p1", true, false, false, true); + appendShareBar("p1", messageOptions.isLoggedIn, true, false, false, true); document.getElementById("p1").classList.remove("processingGif"); } } } else { imageCount = message.image_data.length; message.image_data.forEach(function (element) { - addImage(element, false, messageOptions.canShare, false); + addImage(element, messageOptions.isLoggedIn, messageOptions.canShare, false, false); }); } break; @@ -590,18 +685,23 @@ function testInBrowser(test) { } else if (test === 1) { imageCount = 2; //addImage({ localPath: 'http://lorempixel.com/553/255' }); - addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, false, true, true, false, false, true); - addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, false, true, true, false, false, true); + addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, true, true, false, true, false, false, true); + addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, true, true, false, true, false, false, true); } else if (test === 2) { - addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, false, true, true, false, false, true); - addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, false, true, true, false, false, true); + addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, true, true, false, true, false, false, true); + addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, true, true, false, true, false, false, true); showConfirmationMessage("p0", 'blast'); showConfirmationMessage("p1", 'hifi'); } else if (test === 3) { imageCount = 2; //addImage({ localPath: 'http://lorempixel.com/553/255' }); - addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, false, true, true, false, false, true); - addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, false, true, true, false, false, true); + addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, true, true, false, true, false, false, true); + addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, true, true, false, true, false, false, true); showUploadingMessage("p0", 'hifi'); - } + } else if (test === 4) { + imageCount = 2; + //addImage({ localPath: 'http://lorempixel.com/553/255' }); + addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.jpg', story_id: 1338 }, false, true, false, true, false, false, true); + addImage({ localPath: 'D:/Dropbox/Screenshots/High Fidelity Snapshots/hifi-snap-by-zfox-on-2017-05-01_13-28-58.gif', story_id: 1337 }, false, true, false, true, false, false, true); +} } diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index b93595f0b4..a4e18d2869 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -29,12 +29,13 @@ var button = tablet.addButton({ sortOrder: 5 }); -var snapshotOptions; +var snapshotOptions = {}; var imageData = []; var storyIDsToMaybeDelete = []; var shareAfterLogin = false; -var snapshotToShareAfterLogin; +var snapshotToShareAfterLogin = []; var METAVERSE_BASE = location.metaverseServerUrl; +var isLoggedIn; // It's totally unnecessary to return to C++ to perform many of these requests, such as DELETEing an old story, // POSTING a new one, PUTTING a new audience, or GETTING story data. It's far more efficient to do all of that within JS @@ -108,7 +109,6 @@ function onMessage(message) { return; } - var isLoggedIn; switch (message.action) { case 'ready': // DOM is ready and page has loaded tablet.emitScriptEvent(JSON.stringify({ @@ -141,6 +141,9 @@ function onMessage(message) { Settings.setValue("previousAnimatedSnapHifiSharingDisabled", false); } break; + case 'login': + openLoginWindow(); + break; case 'chooseSnapshotLocation': var snapshotPath = Window.browseDir("Choose Snapshots Directory", "", ""); @@ -177,18 +180,19 @@ function onMessage(message) { print('Sharing snapshot with audience "for_url":', message.data); Window.shareSnapshot(message.data, message.href || href); } else { - // TODO + shareAfterLogin = true; + snapshotToShareAfterLogin.push({ path: message.data, href: message.href || href }); } break; case 'blastToConnections': isLoggedIn = Account.isLoggedIn(); - if (message.isGif) { - Settings.setValue("previousAnimatedSnapBlastingDisabled", true); - } else { - Settings.setValue("previousStillSnapBlastingDisabled", true); - } - if (isLoggedIn) { + if (message.isGif) { + Settings.setValue("previousAnimatedSnapBlastingDisabled", true); + } else { + Settings.setValue("previousStillSnapBlastingDisabled", true); + } + print('Uploading new story for announcement!'); request({ @@ -234,20 +238,16 @@ function onMessage(message) { }); } }); - - } else { - openLoginWindow(); } break; case 'shareSnapshotWithEveryone': isLoggedIn = Account.isLoggedIn(); - if (message.isGif) { - Settings.setValue("previousAnimatedSnapHifiSharingDisabled", true); - } else { - Settings.setValue("previousStillSnapHifiSharingDisabled", true); - } - if (isLoggedIn) { + if (message.isGif) { + Settings.setValue("previousAnimatedSnapHifiSharingDisabled", true); + } else { + Settings.setValue("previousStillSnapHifiSharingDisabled", true); + } print('Modifying audience of story ID', message.story_id, "to 'for_feed'"); var requestBody = { audience: "for_feed" @@ -275,10 +275,6 @@ function onMessage(message) { print("SUCCESS changing audience" + (message.isAnnouncement ? " and posting announcement!" : "!")); } }); - } else { - openLoginWindow(); - shareAfterLogin = true; - snapshotToShareAfterLogin = { path: message.data, href: message.href || href }; } break; case 'removeFromStoryIDsToMaybeDelete': @@ -291,6 +287,42 @@ function onMessage(message) { } } +function fillImageDataFromPrevious() { + isLoggedIn = Account.isLoggedIn(); + var previousStillSnapPath = Settings.getValue("previousStillSnapPath"); + var previousStillSnapStoryID = Settings.getValue("previousStillSnapStoryID"); + var previousStillSnapBlastingDisabled = Settings.getValue("previousStillSnapBlastingDisabled"); + var previousStillSnapHifiSharingDisabled = Settings.getValue("previousStillSnapHifiSharingDisabled"); + var previousAnimatedSnapPath = Settings.getValue("previousAnimatedSnapPath"); + var previousAnimatedSnapStoryID = Settings.getValue("previousAnimatedSnapStoryID"); + var previousAnimatedSnapBlastingDisabled = Settings.getValue("previousAnimatedSnapBlastingDisabled"); + var previousAnimatedSnapHifiSharingDisabled = Settings.getValue("previousAnimatedSnapHifiSharingDisabled"); + snapshotOptions = { + containsGif: previousAnimatedSnapPath !== "", + processingGif: false, + shouldUpload: false, + canBlast: location.domainId === Settings.getValue("previousSnapshotDomainID"), + isLoggedIn: isLoggedIn + }; + imageData = []; + if (previousStillSnapPath !== "") { + imageData.push({ + localPath: previousStillSnapPath, + story_id: previousStillSnapStoryID, + blastButtonDisabled: previousStillSnapBlastingDisabled, + hifiButtonDisabled: previousStillSnapHifiSharingDisabled + }); + } + if (previousAnimatedSnapPath !== "") { + imageData.push({ + localPath: previousAnimatedSnapPath, + story_id: previousAnimatedSnapStoryID, + blastButtonDisabled: previousAnimatedSnapBlastingDisabled, + hifiButtonDisabled: previousAnimatedSnapHifiSharingDisabled + }); + } +} + var SNAPSHOT_REVIEW_URL = Script.resolvePath("html/SnapshotReview.html"); var isInSnapshotReview = false; var shouldActivateButton = false; @@ -300,37 +332,7 @@ function onButtonClicked() { tablet.gotoHomeScreen(); } else { shouldActivateButton = true; - var previousStillSnapPath = Settings.getValue("previousStillSnapPath"); - var previousStillSnapStoryID = Settings.getValue("previousStillSnapStoryID"); - var previousStillSnapBlastingDisabled = Settings.getValue("previousStillSnapBlastingDisabled"); - var previousStillSnapHifiSharingDisabled = Settings.getValue("previousStillSnapHifiSharingDisabled"); - var previousAnimatedSnapPath = Settings.getValue("previousAnimatedSnapPath"); - var previousAnimatedSnapStoryID = Settings.getValue("previousAnimatedSnapStoryID"); - var previousAnimatedSnapBlastingDisabled = Settings.getValue("previousAnimatedSnapBlastingDisabled"); - var previousAnimatedSnapHifiSharingDisabled = Settings.getValue("previousAnimatedSnapHifiSharingDisabled"); - snapshotOptions = { - containsGif: previousAnimatedSnapPath !== "", - processingGif: false, - shouldUpload: false, - canBlast: location.domainId === Settings.getValue("previousSnapshotDomainID") - } - imageData = []; - if (previousStillSnapPath !== "") { - imageData.push({ - localPath: previousStillSnapPath, - story_id: previousStillSnapStoryID, - blastButtonDisabled: previousStillSnapBlastingDisabled, - hifiButtonDisabled: previousStillSnapHifiSharingDisabled - }); - } - if (previousAnimatedSnapPath !== "") { - imageData.push({ - localPath: previousAnimatedSnapPath, - story_id: previousAnimatedSnapStoryID, - blastButtonDisabled: previousAnimatedSnapBlastingDisabled, - hifiButtonDisabled: previousAnimatedSnapHifiSharingDisabled - }); - } + fillImageDataFromPrevious(); tablet.gotoWebScreen(SNAPSHOT_REVIEW_URL); tablet.webEventReceived.connect(onMessage); HMD.openTablet(); @@ -453,6 +455,7 @@ function isDomainOpen(id, callback) { } function stillSnapshotTaken(pathStillSnapshot, notify) { + isLoggedIn = Account.isLoggedIn(); // show hud Reticle.visible = reticleVisible; Reticle.allowMouseCapture = true; @@ -481,7 +484,8 @@ function stillSnapshotTaken(pathStillSnapshot, notify) { snapshotOptions = { containsGif: false, processingGif: false, - canShare: canShare + canShare: canShare, + isLoggedIn: isLoggedIn }; imageData = [{ localPath: pathStillSnapshot, href: href }]; tablet.emitScriptEvent(JSON.stringify({ @@ -496,6 +500,7 @@ function stillSnapshotTaken(pathStillSnapshot, notify) { function processingGifStarted(pathStillSnapshot) { Window.processingGifStarted.disconnect(processingGifStarted); Window.processingGifCompleted.connect(processingGifCompleted); + isLoggedIn = Account.isLoggedIn(); // show hud Reticle.visible = reticleVisible; Reticle.allowMouseCapture = true; @@ -515,7 +520,8 @@ function processingGifStarted(pathStillSnapshot) { containsGif: true, processingGif: true, loadingGifPath: Script.resolvePath(Script.resourcesPath() + 'icons/loadingDark.gif'), - canShare: canShare + canShare: canShare, + isLoggedIn: isLoggedIn }; imageData = [{ localPath: pathStillSnapshot, href: href }]; tablet.emitScriptEvent(JSON.stringify({ @@ -528,6 +534,7 @@ function processingGifStarted(pathStillSnapshot) { } function processingGifCompleted(pathAnimatedSnapshot) { + isLoggedIn = Account.isLoggedIn(); Window.processingGifCompleted.disconnect(processingGifCompleted); if (!buttonConnected) { button.clicked.connect(onButtonClicked); @@ -540,7 +547,8 @@ function processingGifCompleted(pathAnimatedSnapshot) { snapshotOptions = { containsGif: true, processingGif: false, - canShare: canShare + canShare: canShare, + isLoggedIn: isLoggedIn }; imageData = [{ localPath: pathAnimatedSnapshot, href: href }]; tablet.emitScriptEvent(JSON.stringify({ @@ -576,10 +584,25 @@ function onTabletScreenChanged(type, url) { } } function onUsernameChanged() { - if (shareAfterLogin && Account.isLoggedIn()) { - print('Sharing snapshot after login:', snapshotToShareAfterLogin.path); - Window.shareSnapshot(snapshotToShareAfterLogin.path, snapshotToShareAfterLogin.href); - shareAfterLogin = false; + fillImageDataFromPrevious(); + isDomainOpen(Settings.getValue("previousSnapshotDomainID"), function (canShare) { + tablet.emitScriptEvent(JSON.stringify({ + type: "snapshot", + action: "showPreviousImages", + options: snapshotOptions, + image_data: imageData, + canShare: canShare + })); + }); + if (isLoggedIn) { + if (shareAfterLogin) { + snapshotToShareAfterLogin.forEach(function (element) { + print('Uploading snapshot after login:', element.path); + Window.shareSnapshot(element.path, element.href); + }); + shareAfterLogin = false; + snapshotToShareAfterLogin = []; + } } } function snapshotLocationSet(location) { @@ -595,7 +618,7 @@ button.clicked.connect(onButtonClicked); buttonConnected = true; Window.snapshotShared.connect(snapshotUploaded); tablet.screenChanged.connect(onTabletScreenChanged); -Account.usernameChanged.connect(onUsernameChanged); +GlobalServices.myUsernameChanged.connect(onUsernameChanged); Snapshot.snapshotLocationSet.connect(snapshotLocationSet); Script.scriptEnding.connect(function () { if (buttonConnected) { From 2ad3346697355a0ca759a92178b30c38c5efc1df Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 4 May 2017 16:05:20 -0700 Subject: [PATCH 25/26] Are there bugs? --- scripts/system/html/css/SnapshotReview.css | 1 - scripts/system/html/js/SnapshotReview.js | 22 +++++++------- scripts/system/snapshot.js | 34 ++++++++++++++-------- 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/scripts/system/html/css/SnapshotReview.css b/scripts/system/html/css/SnapshotReview.css index 69de4bb147..218eb8c9a5 100644 --- a/scripts/system/html/css/SnapshotReview.css +++ b/scripts/system/html/css/SnapshotReview.css @@ -198,7 +198,6 @@ input[type=button].naked:active { font-family: Raleway-Regular; font-weight: 500; font-size: 16px; - text-align: right; color: white; } /* diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index dc0d319d16..25bc25b776 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -217,8 +217,8 @@ function createShareBar(parentID, isLoggedIn, canShare, isGif, blastButtonDisabl '' + '' + '' + - '' + '' + - '' + '' + - '