diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f9424500a7..8113057210 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1321,7 +1321,8 @@ void Application::processAvatarURLsMessage(unsigned char* packetData, size_t dat QMetaObject::invokeMethod(avatar->getVoxels(), "setVoxelURL", Q_ARG(QUrl, voxelURL)); // use this timing to as the data-server for an updated mesh for this avatar (if we have UUID) - DataServerClient::getValueForKeyAndUUID(DataServerKey::FaceMeshURL, avatar->getUUID()); + DataServerClient::getValuesForKeysAndUUID(QStringList() << DataServerKey::FaceMeshURL << DataServerKey::SkeletonURL, + avatar->getUUID()); } void Application::processAvatarFaceVideoMessage(unsigned char* packetData, size_t dataBytes) { @@ -1652,6 +1653,7 @@ void Application::init() { if (!_profile.getUsername().isEmpty()) { // we have a username for this avatar, ask the data-server for the mesh URL for this avatar DataServerClient::getClientValueForKey(DataServerKey::FaceMeshURL); + DataServerClient::getClientValueForKey(DataServerKey::SkeletonURL); } // Set up VoxelSystem after loading preferences so we can get the desired max voxel count diff --git a/interface/src/DataServerClient.cpp b/interface/src/DataServerClient.cpp index 26e297375c..ef5293fa54 100644 --- a/interface/src/DataServerClient.cpp +++ b/interface/src/DataServerClient.cpp @@ -148,6 +148,26 @@ void DataServerClient::processSendFromDataServer(unsigned char* packetData, int } } } + } else if (keyList[i] == DataServerKey::SkeletonURL) { + + if (userUUID.isNull() || userUUID == Application::getInstance()->getProfile()->getUUID()) { + qDebug("Changing user's skeleton URL to %s\n", valueList[0].toLocal8Bit().constData()); + Application::getInstance()->getProfile()->setSkeletonModelURL(QUrl(valueList[0])); + } else { + // skeleton URL for a UUID, find avatar in our list + NodeList* nodeList = NodeList::getInstance(); + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { + if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) { + Avatar* avatar = (Avatar *) node->getLinkedData(); + + if (avatar->getUUID() == userUUID) { + QMetaObject::invokeMethod(&avatar->getBody(), + "setSkeletonModelURL", + Q_ARG(QUrl, QUrl(valueList[0]))); + } + } + } + } } else if (keyList[i] == DataServerKey::Domain && keyList[i + 1] == DataServerKey::Position && valueList[i] != " " && valueList[i + 1] != " ") { diff --git a/interface/src/DataServerClient.h b/interface/src/DataServerClient.h index dabb1c822c..2331cae1e9 100644 --- a/interface/src/DataServerClient.h +++ b/interface/src/DataServerClient.h @@ -38,6 +38,7 @@ private: namespace DataServerKey { const QString Domain = "domain"; const QString FaceMeshURL = "mesh"; + const QString SkeletonURL = "skeleton"; const QString Position = "position"; const QString UUID = "uuid"; } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index da7ec8f941..013f1d461b 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -862,6 +862,17 @@ void Menu::editPreferences() { faceModelURL.toString().toLocal8Bit().constData()); } + QUrl skeletonModelURL(skeletonURLEdit->text()); + + if (skeletonModelURL.toString() != skeletonURLString) { + // change the skeletonModelURL in the profile, it will also update this user's Body + applicationInstance->getProfile()->setSkeletonModelURL(skeletonModelURL); + + // send the new skeleton model URL to the data-server (if we have a client UUID) + DataServerClient::putValueForKey(DataServerKey::SkeletonURL, + skeletonModelURL.toString().toLocal8Bit().constData()); + } + QUrl avatarVoxelURL(avatarURL->text()); applicationInstance->getAvatar()->getVoxels()->setVoxelURL(avatarVoxelURL); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index ded8bac53f..f027df2c25 100755 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -756,7 +756,7 @@ void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) { if (alpha > 0.0f) { _head.getFace().render(1.0f); } - } else if (renderAvatarBalls || !_voxels.getVoxelURL().isValid()) { + } else if (renderAvatarBalls || !(_voxels.getVoxelURL().isValid() || _body.isActive())) { // Render the body as balls and cones glm::vec3 skinColor(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2]); glm::vec3 darkSkinColor(DARK_SKIN_COLOR[0], DARK_SKIN_COLOR[1], DARK_SKIN_COLOR[2]); @@ -821,7 +821,9 @@ void Avatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) { // Render the body's voxels and head float alpha = getBallRenderAlpha(BODY_BALL_HEAD_BASE, lookingInMirror); if (alpha > 0.0f) { - _voxels.render(false); + if (!_body.render(alpha)) { + _voxels.render(false); + } _head.render(alpha, false); } } diff --git a/interface/src/avatar/Body.cpp b/interface/src/avatar/Body.cpp index 684c9deb56..20f30d7b3f 100644 --- a/interface/src/avatar/Body.cpp +++ b/interface/src/avatar/Body.cpp @@ -52,7 +52,11 @@ void Body::simulate(float deltaTime) { } bool Body::render(float alpha) { - return false; + if (_jointStates.isEmpty()) { + return false; + } + + return true; } void Body::setSkeletonModelURL(const QUrl& url) { @@ -61,6 +65,5 @@ void Body::setSkeletonModelURL(const QUrl& url) { return; } _skeletonModelURL = url; - _skeletonGeometry = Application::getInstance()->getGeometryCache()->getGeometry(url); } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e1c18089fd..01a6c356fc 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -322,6 +322,7 @@ void MyAvatar::simulate(float deltaTime, Transmitter* transmitter) { _head.setScale(_scale); _head.setSkinColor(glm::vec3(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2])); _head.simulate(deltaTime, true); + _body.simulate(deltaTime); _hand.simulate(deltaTime, true); const float WALKING_SPEED_THRESHOLD = 0.2f; @@ -610,7 +611,7 @@ void MyAvatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) { if (alpha > 0.0f) { _head.getFace().render(1.0f); } - } else if (renderAvatarBalls || !_voxels.getVoxelURL().isValid()) { + } else if (renderAvatarBalls || !(_voxels.getVoxelURL().isValid() || _body.isActive())) { // Render the body as balls and cones glm::vec3 skinColor(SKIN_COLOR[0], SKIN_COLOR[1], SKIN_COLOR[2]); glm::vec3 darkSkinColor(DARK_SKIN_COLOR[0], DARK_SKIN_COLOR[1], DARK_SKIN_COLOR[2]); @@ -685,7 +686,9 @@ void MyAvatar::renderBody(bool lookingInMirror, bool renderAvatarBalls) { // Render the body's voxels and head float alpha = getBallRenderAlpha(BODY_BALL_HEAD_BASE, lookingInMirror); if (alpha > 0.0f) { - _voxels.render(false); + if (!_body.render(alpha)) { + _voxels.render(false); + } _head.render(alpha, true); } } diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index 3d6d7ae3a6..8e4b95f5c3 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -431,15 +431,16 @@ public: int parentIndex; }; -glm::mat4 getGlobalTransform(const QMultiHash& parentMap, const QHash& models, qint64 nodeID) { +glm::mat4 getGlobalTransform(const QMultiHash& parentMap, + const QHash& models, QString nodeID) { glm::mat4 globalTransform; - while (nodeID != 0) { + while (!nodeID.isNull()) { const Model& model = models.value(nodeID); globalTransform = model.preRotation * glm::mat4_cast(model.rotation) * model.postRotation * globalTransform; - QList parentIDs = parentMap.values(nodeID); - nodeID = 0; - foreach (qint64 parentID, parentIDs) { + QList parentIDs = parentMap.values(nodeID); + nodeID = QString(); + foreach (const QString& parentID, parentIDs) { if (models.contains(parentID)) { nodeID = parentID; break; @@ -452,7 +453,7 @@ glm::mat4 getGlobalTransform(const QMultiHash& parentMap, const class ExtractedBlendshape { public: - qint64 id; + QString id; FBXBlendshape blendshape; }; @@ -482,13 +483,13 @@ public: glm::mat4 transformLink; }; -void appendModelIDs(qint64 parentID, const QMultiHash& childMap, - QHash& models, QVector& modelIDs) { - if (parentID != 0) { +void appendModelIDs(const QString& parentID, const QMultiHash& childMap, + QHash& models, QVector& modelIDs) { + if (models.contains(parentID)) { modelIDs.append(parentID); } int parentIndex = modelIDs.size() - 1; - foreach (qint64 childID, childMap.values(parentID)) { + foreach (const QString& childID, childMap.values(parentID)) { if (models.contains(childID)) { models[childID].parentIndex = parentIndex; appendModelIDs(childID, childMap, models, modelIDs); @@ -497,24 +498,24 @@ void appendModelIDs(qint64 parentID, const QMultiHash& childMap, } FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) { - QHash meshes; + QHash meshes; QVector blendshapes; - QMultiHash parentMap; - QMultiHash childMap; - QHash models; - QHash clusters; - QHash textureFilenames; - QHash materials; - QHash diffuseTextures; - QHash bumpTextures; + QMultiHash parentMap; + QMultiHash childMap; + QHash models; + QHash clusters; + QHash textureFilenames; + QHash materials; + QHash diffuseTextures; + QHash bumpTextures; QVariantHash joints = mapping.value("joint").toHash(); QByteArray jointEyeLeftName = joints.value("jointEyeLeft", "jointEyeLeft").toByteArray(); QByteArray jointEyeRightName = joints.value("jointEyeRight", "jointEyeRight").toByteArray(); QByteArray jointNeckName = joints.value("jointNeck", "jointNeck").toByteArray(); - qint64 jointEyeLeftID = 0; - qint64 jointEyeRightID = 0; - qint64 jointNeckID = 0; + QString jointEyeLeftID; + QString jointEyeRightID; + QString jointNeckID; QVariantHash blendshapeMappings = mapping.value("bs").toHash(); QHash > blendshapeIndices; @@ -534,7 +535,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } } } - QHash > blendshapeChannelIndices; + QHash > blendshapeChannelIndices; foreach (const FBXNode& child, node.children) { if (child.name == "Objects") { @@ -653,10 +654,10 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) beginIndex = endIndex; } } - meshes.insert(object.properties.at(0).value(), mesh); + meshes.insert(object.properties.at(0).toString(), mesh); } else { // object.properties.at(2) == "Shape" - ExtractedBlendshape extracted = { object.properties.at(0).value() }; + ExtractedBlendshape extracted = { object.properties.at(0).toString() }; foreach (const FBXNode& data, object.children) { if (data.name == "Indexes") { @@ -678,13 +679,13 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) QByteArray name = object.properties.at(1).toByteArray(); name = name.left(name.indexOf('\0')); if (name == jointEyeLeftName || name == "EyeL" || name == "joint_Leye") { - jointEyeLeftID = object.properties.at(0).value(); + jointEyeLeftID = object.properties.at(0).toString(); } else if (name == jointEyeRightName || name == "EyeR" || name == "joint_Reye") { - jointEyeRightID = object.properties.at(0).value(); + jointEyeRightID = object.properties.at(0).toString(); } else if (name == jointNeckName || name == "NeckRot" || name == "joint_neck") { - jointNeckID = object.properties.at(0).value(); + jointNeckID = object.properties.at(0).toString(); } glm::vec3 translation; glm::vec3 preRotation, rotation, postRotation; @@ -740,7 +741,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) model.rotation = glm::quat(glm::radians(rotation)); model.postRotation = glm::mat4_cast(glm::quat(glm::radians(postRotation))) * glm::translate(-rotationPivot) * glm::translate(scalePivot) * glm::scale(scale) * glm::translate(-scalePivot); - models.insert(object.properties.at(0).value(), model); + models.insert(object.properties.at(0).toString(), model); } else if (object.name == "Texture") { foreach (const FBXNode& subobject, object.children) { @@ -748,7 +749,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) // trim off any path information QByteArray filename = subobject.properties.at(0).toByteArray(); filename = filename.mid(qMax(filename.lastIndexOf('\\'), filename.lastIndexOf('/')) + 1); - textureFilenames.insert(object.properties.at(0).value(), filename); + textureFilenames.insert(object.properties.at(0).toString(), filename); } } } else if (object.name == "Material") { @@ -774,7 +775,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } } } - materials.insert(object.properties.at(0).value(), material); + materials.insert(object.properties.at(0).toString(), material); } else if (object.name == "Deformer") { if (object.properties.at(2) == "Cluster") { @@ -791,7 +792,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) cluster.transformLink = createMat4(values); } } - clusters.insert(object.properties.at(0).value(), cluster); + clusters.insert(object.properties.at(0).toString(), cluster); } else if (object.properties.at(2) == "BlendShapeChannel") { QByteArray name = object.properties.at(1).toByteArray(); @@ -800,26 +801,26 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) // try everything after the dot name = name.mid(name.lastIndexOf('.') + 1); } - blendshapeChannelIndices.insert(object.properties.at(0).value(), + blendshapeChannelIndices.insert(object.properties.at(0).toString(), blendshapeIndices.value(name)); } } } } else if (child.name == "Connections") { foreach (const FBXNode& connection, child.children) { - if (connection.name == "C") { + if (connection.name == "C" || connection.name == "Connect") { if (connection.properties.at(0) == "OP") { if (connection.properties.at(3) == "DiffuseColor") { - diffuseTextures.insert(connection.properties.at(2).value(), - connection.properties.at(1).value()); + diffuseTextures.insert(connection.properties.at(2).toString(), + connection.properties.at(1).toString()); } else if (connection.properties.at(3) == "Bump") { - bumpTextures.insert(connection.properties.at(2).value(), - connection.properties.at(1).value()); + bumpTextures.insert(connection.properties.at(2).toString(), + connection.properties.at(1).toString()); } } - parentMap.insert(connection.properties.at(1).value(), connection.properties.at(2).value()); - childMap.insert(connection.properties.at(2).value(), connection.properties.at(1).value()); + parentMap.insert(connection.properties.at(1).toString(), connection.properties.at(2).toString()); + childMap.insert(connection.properties.at(2).toString(), connection.properties.at(1).toString()); } } } @@ -827,10 +828,10 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) // assign the blendshapes to their corresponding meshes foreach (const ExtractedBlendshape& extracted, blendshapes) { - qint64 blendshapeChannelID = parentMap.value(extracted.id); + QString blendshapeChannelID = parentMap.value(extracted.id); QPair index = blendshapeChannelIndices.value(blendshapeChannelID); - qint64 blendshapeID = parentMap.value(blendshapeChannelID); - qint64 meshID = parentMap.value(blendshapeID); + QString blendshapeID = parentMap.value(blendshapeChannelID); + QString meshID = parentMap.value(blendshapeID); FBXMesh& mesh = meshes[meshID]; mesh.blendshapes.resize(max(mesh.blendshapes.size(), index.first + 1)); mesh.blendshapes[index.first] = extracted.blendshape; @@ -854,11 +855,26 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) glm::scale(offsetScale, offsetScale, offsetScale); // get the list of models in depth-first traversal order - QVector modelIDs; - appendModelIDs(0, childMap, models, modelIDs); + QVector modelIDs; + if (!models.isEmpty()) { + QString top = models.constBegin().key(); + forever { + foreach (const QString& name, parentMap.values(top)) { + if (models.contains(name)) { + top = name; + goto outerContinue; + } + } + top = parentMap.value(top); + break; + + outerContinue: ; + } + appendModelIDs(top, childMap, models, modelIDs); + } // convert the models to joints - foreach (qint64 modelID, modelIDs) { + foreach (const QString& modelID, modelIDs) { const Model& model = models[modelID]; FBXJoint joint; joint.parentIndex = model.parentIndex; @@ -889,17 +905,17 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) QVariantHash springs = mapping.value("spring").toHash(); QVariant defaultSpring = springs.value("default"); - for (QHash::iterator it = meshes.begin(); it != meshes.end(); it++) { + for (QHash::iterator it = meshes.begin(); it != meshes.end(); it++) { FBXMesh& mesh = it.value(); // accumulate local transforms - qint64 modelID = parentMap.value(it.key()); + QString modelID = parentMap.value(it.key()); mesh.springiness = springs.value(models.value(modelID).name, defaultSpring).toFloat(); glm::mat4 modelTransform = getGlobalTransform(parentMap, models, modelID); // look for textures, material properties int partIndex = 0; - foreach (qint64 childID, childMap.values(modelID)) { + foreach (const QString& childID, childMap.values(modelID)) { if (!materials.contains(childID) || partIndex >= mesh.parts.size()) { continue; } @@ -908,21 +924,21 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) part.diffuseColor = material.diffuse; part.specularColor = material.specular; part.shininess = material.shininess; - qint64 diffuseTextureID = diffuseTextures.value(childID); - if (diffuseTextureID != 0) { + QString diffuseTextureID = diffuseTextures.value(childID); + if (!diffuseTextureID.isNull()) { part.diffuseFilename = textureFilenames.value(diffuseTextureID); } - qint64 bumpTextureID = bumpTextures.value(childID); - if (bumpTextureID != 0) { + QString bumpTextureID = bumpTextures.value(childID); + if (!bumpTextureID.isNull()) { part.normalFilename = textureFilenames.value(bumpTextureID); } } // find the clusters with which the mesh is associated mesh.isEye = false; - QVector clusterIDs; - foreach (qint64 childID, childMap.values(it.key())) { - foreach (qint64 clusterID, childMap.values(childID)) { + QVector clusterIDs; + foreach (const QString& childID, childMap.values(it.key())) { + foreach (const QString& clusterID, childMap.values(childID)) { if (!clusters.contains(clusterID)) { continue; } @@ -930,7 +946,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) const Cluster& cluster = clusters[clusterID]; clusterIDs.append(clusterID); - qint64 jointID = childMap.value(clusterID); + QString jointID = childMap.value(clusterID); if (jointID == jointEyeLeftID || jointID == jointEyeRightID) { mesh.isEye = true; } @@ -954,7 +970,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) mesh.clusterIndices.resize(mesh.vertices.size()); mesh.clusterWeights.resize(mesh.vertices.size()); for (int i = 0; i < clusterIDs.size(); i++) { - qint64 clusterID = clusterIDs.at(i); + QString clusterID = clusterIDs.at(i); const Cluster& cluster = clusters[clusterID]; for (int j = 0; j < cluster.indices.size(); j++) { diff --git a/interface/src/renderer/GeometryCache.h b/interface/src/renderer/GeometryCache.h index ac6669ad2f..d880d25855 100644 --- a/interface/src/renderer/GeometryCache.h +++ b/interface/src/renderer/GeometryCache.h @@ -57,7 +57,7 @@ public: NetworkGeometry(const QUrl& url); ~NetworkGeometry(); - bool isLoaded() const { return !_meshes.isEmpty(); } + bool isLoaded() const { return !_geometry.joints.isEmpty(); } const FBXGeometry& getFBXGeometry() const { return _geometry; } const QVector& getMeshes() const { return _meshes; }