diff --git a/BUILD.md b/BUILD.md index a5ee32262f..ad365fcde2 100644 --- a/BUILD.md +++ b/BUILD.md @@ -5,6 +5,7 @@ * [OpenSSL](https://www.openssl.org/related/binaries.html) ~> 1.0.1m * IMPORTANT: Using the recommended version of OpenSSL is critical to avoid security vulnerabilities. * [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional) +* [zlib](http://www.zlib.net/) ####CMake External Project Dependencies diff --git a/BUILD_OSX.md b/BUILD_OSX.md index 60fbb7cf92..9d1357d672 100644 --- a/BUILD_OSX.md +++ b/BUILD_OSX.md @@ -1,7 +1,7 @@ Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only OS X specific instructions are found in this file. ###Homebrew -[Homebrew](http://brew.sh/) is an excellent package manager for OS X. It makes install of all hifi dependencies very simple. +[Homebrew](http://brew.sh/) is an excellent package manager for OS X. It makes install of all High Fidelity dependencies very simple. brew tap highfidelity/homebrew-formulas brew install cmake openssl diff --git a/BUILD_WIN.md b/BUILD_WIN.md index e905a83ace..b6ec31b713 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -75,6 +75,16 @@ To prevent these problems, install OpenSSL yourself. Download the following bina Install OpenSSL into the Windows system directory, to make sure that Qt uses the version that you've just installed, and not some other version. +####zlib + +Install zlib from + + [Zlib for Windows](http://gnuwin32.sourceforge.net/packages/zlib.htm) + +and fix a header file, as described here: + + [zlib zconf.h bug](http://sourceforge.net/p/gnuwin32/bugs/169/) + ###Build High Fidelity using Visual Studio Follow the same build steps from the CMake section of [BUILD.md](BUILD.md), but pass a different generator to CMake. diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index c13de0449e..4e5b563fc6 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -579,7 +579,6 @@ const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer void DomainServer::processConnectRequestPacket(QSharedPointer packet) { NodeType_t nodeType; HifiSockAddr publicSockAddr, localSockAddr; - if (packet->getPayloadSize() == 0) { return; @@ -625,31 +624,62 @@ void DomainServer::processConnectRequestPacket(QSharedPointer packet) return; } } - } QList nodeInterestList; QString username; QByteArray usernameSignature; - - packetStream >> nodeInterestList >> username >> usernameSignature; - + auto limitedNodeList = DependencyManager::get(); - + + packetStream >> nodeInterestList; + + if (packet->bytesLeftToRead() > 0) { + // try to verify username + packetStream >> username; + } + + bool isRestrictingAccess = + _settingsManager.valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool(); + + // we always let in a user who is sending a packet from our local socket or from the localhost address + bool isLocalUser = (senderSockAddr.getAddress() == DependencyManager::get()->getLocalSockAddr().getAddress() || senderSockAddr.getAddress() == QHostAddress::LocalHost); + + if (isRestrictingAccess && !isLocalUser) { + if (!username.isEmpty()) { + // if there's a username, try to unpack username signature + packetStream >> usernameSignature; + + if (usernameSignature.isEmpty()) { + // if user didn't include usernameSignature in connect request, send a connectionToken packet + QUuid& connectionToken = _connectionTokenHash[username.toLower()]; + + if (connectionToken.isNull()) { + connectionToken = QUuid::createUuid(); + } + + static auto connectionTokenPacket = NLPacket::create(PacketType::DomainServerConnectionToken, NUM_BYTES_RFC4122_UUID); + connectionTokenPacket->reset(); + connectionTokenPacket->write(connectionToken.toRfc4122()); + limitedNodeList->sendUnreliablePacket(*connectionTokenPacket, packet->getSenderSockAddr()); + return; + } + } + } + QString reason; if (!isAssignment && !shouldAllowConnectionFromNode(username, usernameSignature, senderSockAddr, reason)) { // this is an agent and we've decided we won't let them connect - send them a packet to deny connection - QByteArray utfString = reason.toUtf8(); quint16 payloadSize = utfString.size(); auto connectionDeniedPacket = NLPacket::create(PacketType::DomainConnectionDenied, payloadSize + sizeof(payloadSize)); - connectionDeniedPacket->writePrimitive(payloadSize); - connectionDeniedPacket->write(utfString); - + if (payloadSize > 0) { + connectionDeniedPacket->writePrimitive(payloadSize); + connectionDeniedPacket->write(utfString); + } // tell client it has been refused. limitedNodeList->sendPacket(std::move(connectionDeniedPacket), senderSockAddr); - return; } @@ -736,6 +766,7 @@ void DomainServer::processConnectRequestPacket(QSharedPointer packet) } } + void DomainServer::processListRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode) { NodeType_t throwawayNodeType; @@ -766,52 +797,61 @@ unsigned int DomainServer::countConnectedUsers() { } -bool DomainServer::verifyUsersKey(const QString& username, - const QByteArray& usernameSignature, - QString& reasonReturn) { +bool DomainServer::verifyUserSignature(const QString& username, + const QByteArray& usernameSignature, + QString& reasonReturn) { + // it's possible this user can be allowed to connect, but we need to check their username signature - QByteArray publicKeyArray = _userPublicKeys.value(username); - if (!publicKeyArray.isEmpty()) { + + const QUuid& connectionToken = _connectionTokenHash.value(username.toLower()); + + if (!publicKeyArray.isEmpty() && !connectionToken.isNull()) { // if we do have a public key for the user, check for a signature match const unsigned char* publicKeyData = reinterpret_cast(publicKeyArray.constData()); // first load up the public key into an RSA struct RSA* rsaPublicKey = d2i_RSA_PUBKEY(NULL, &publicKeyData, publicKeyArray.size()); - + + QByteArray lowercaseUsername = username.toLower().toUtf8(); + QByteArray usernameWithToken = QCryptographicHash::hash(lowercaseUsername.append(connectionToken.toRfc4122()), + QCryptographicHash::Sha256); + if (rsaPublicKey) { QByteArray decryptedArray(RSA_size(rsaPublicKey), 0); - int decryptResult = - RSA_public_decrypt(usernameSignature.size(), - reinterpret_cast(usernameSignature.constData()), - reinterpret_cast(decryptedArray.data()), - rsaPublicKey, RSA_PKCS1_PADDING); + int decryptResult = RSA_verify(NID_sha256, + reinterpret_cast(usernameWithToken.constData()), + usernameWithToken.size(), + reinterpret_cast(usernameSignature.constData()), + usernameSignature.size(), + rsaPublicKey); + + if (decryptResult == 1) { + qDebug() << "Username signature matches for" << username << "- allowing connection."; - if (decryptResult != -1) { - if (username.toLower() == decryptedArray) { - qDebug() << "Username signature matches for" << username << "- allowing connection."; + // free up the public key and remove connection token before we return + RSA_free(rsaPublicKey); + _connectionTokenHash.remove(username); - // free up the public key before we return - RSA_free(rsaPublicKey); - - return true; - } else { - qDebug() << "Username signature did not match for" << username << "- denying connection."; - reasonReturn = "Username signature did not match."; - } + return true; + } else { - qDebug() << "Couldn't decrypt user signature for" << username << "- denying connection."; - reasonReturn = "Couldn't decrypt user signature."; + qDebug() << "Error decrypting username signature for " << username << "- denying connection."; + reasonReturn = "Error decrypting username signature."; + // free up the public key, we don't need it anymore + RSA_free(rsaPublicKey); } - // free up the public key, we don't need it anymore - RSA_free(rsaPublicKey); } else { + // we can't let this user in since we couldn't convert their public key to an RSA key we could use qDebug() << "Couldn't convert data to RSA key for" << username << "- denying connection."; reasonReturn = "Couldn't convert data to RSA key."; } + } else { + qDebug() << "Insufficient data to decrypt username signature - denying connection."; + reasonReturn = "Insufficient data"; } requestUserPublicKey(username); // no joy. maybe next time? @@ -823,41 +863,40 @@ bool DomainServer::shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature, const HifiSockAddr& senderSockAddr, QString& reasonReturn) { - + + //TODO: improve flow so these bools aren't declared twice bool isRestrictingAccess = _settingsManager.valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool(); + bool isLocalUser = (senderSockAddr.getAddress() == DependencyManager::get()->getLocalSockAddr().getAddress() || senderSockAddr.getAddress() == QHostAddress::LocalHost); - // we always let in a user who is sending a packet from our local socket or from the localhost address - if (senderSockAddr.getAddress() == DependencyManager::get()->getLocalSockAddr().getAddress() - || senderSockAddr.getAddress() == QHostAddress::LocalHost) { - return true; - } - - if (isRestrictingAccess) { - + if (isRestrictingAccess && !isLocalUser) { QStringList allowedUsers = _settingsManager.valueOrDefaultValueForKeyPath(ALLOWED_USERS_SETTINGS_KEYPATH).toStringList(); - + if (allowedUsers.contains(username, Qt::CaseInsensitive)) { - if (!verifyUsersKey(username, usernameSignature, reasonReturn)) { + if (username.isEmpty()) { + qDebug() << "Connect request denied - no username provided."; + reasonReturn = "No username provided"; + return false; + } + if (!verifyUserSignature(username, usernameSignature, reasonReturn)) { return false; } } else { qDebug() << "Connect request denied for user" << username << "not in allowed users list."; reasonReturn = "User not on whitelist."; - + return false; } } - + // either we aren't restricting users, or this user is in the allowed list - // if this user is in the editors list, exempt them from the max-capacity check const QVariant* allowedEditorsVariant = valueForKeyPath(_settingsManager.getSettingsMap(), ALLOWED_EDITORS_SETTINGS_KEYPATH); QStringList allowedEditors = allowedEditorsVariant ? allowedEditorsVariant->toStringList() : QStringList(); if (allowedEditors.contains(username)) { - if (verifyUsersKey(username, usernameSignature, reasonReturn)) { + if (verifyUserSignature(username, usernameSignature, reasonReturn)) { return true; } } diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 7786fb34ac..7495e080de 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -90,7 +90,7 @@ private: void pingPunchForConnectingPeer(const SharedNetworkPeer& peer); unsigned int countConnectedUsers(); - bool verifyUsersKey (const QString& username, const QByteArray& usernameSignature, QString& reasonReturn); + bool verifyUserSignature (const QString& username, const QByteArray& usernameSignature, QString& reasonReturn); bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature, const HifiSockAddr& senderSockAddr, QString& reasonReturn); @@ -149,6 +149,8 @@ private: QSet _webAuthenticationStateSet; QHash _cookieSessionHash; + + QHash _connectionTokenHash; QHash _userPublicKeys; diff --git a/interface/resources/images/normalFittingScale.dds b/interface/resources/images/normalFittingScale.dds new file mode 100644 index 0000000000..8207ee0981 Binary files /dev/null and b/interface/resources/images/normalFittingScale.dds differ diff --git a/interface/src/avatar/ModelReferential.cpp b/interface/src/avatar/ModelReferential.cpp index 53eae21ff7..46290b17b5 100644 --- a/interface/src/avatar/ModelReferential.cpp +++ b/interface/src/avatar/ModelReferential.cpp @@ -107,7 +107,7 @@ JointReferential::JointReferential(Referential* referential, EntityTree* tree, A EntityItemPointer item = _tree->findEntityByID(_entityID); const Model* model = getModel(item); - if (!isValid() || model == NULL || _jointIndex >= (uint32_t)(model->getJointStateCount())) { + if (isValid() && model != NULL && _jointIndex < (uint32_t)(model->getJointStateCount())) { _lastRefDimension = item->getDimensions(); model->getJointRotationInWorldFrame(_jointIndex, _refRotation); model->getJointPositionInWorldFrame(_jointIndex, _refPosition); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 848838a4e5..3578acaa60 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -47,9 +47,6 @@ #include "Util.h" #include "InterfaceLogging.h" -#include "gpu/GLBackend.h" - - using namespace std; const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 83438ddaa9..83de16ab4f 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -97,7 +97,6 @@ void SkeletonModel::initJointStates(QVector states) { } const float PALM_PRIORITY = DEFAULT_PRIORITY; -const float LEAN_PRIORITY = DEFAULT_PRIORITY; void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { if (_owningAvatar->isMyAvatar()) { diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 30a8952011..80f5ce2b69 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -32,7 +31,6 @@ #include "ui/AvatarInputs.h" const vec4 CONNECTION_STATUS_BORDER_COLOR{ 1.0f, 0.0f, 0.0f, 0.8f }; -const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f; static const float ORTHO_NEAR_CLIP = -1000.0f; static const float ORTHO_FAR_CLIP = 1000.0f; diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index 3bd1f56ba3..58790c4722 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -105,7 +105,7 @@ void BillboardOverlay::render(RenderArgs* args) { batch->setModelTransform(transform); batch->setResourceTexture(0, _texture->getGPUTexture()); - DependencyManager::get()->bindSimpleProgram(*batch, true, true, false, true); + DependencyManager::get()->bindSimpleProgram(*batch, true, false, false, true); DependencyManager::get()->renderQuad( *batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha) diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 0de88efed1..c4acdbb482 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -15,7 +15,6 @@ #include #include -#include #include #include "Application.h" diff --git a/interface/src/ui/overlays/TextOverlay.cpp b/interface/src/ui/overlays/TextOverlay.cpp index e9fda2def8..6926e9b241 100644 --- a/interface/src/ui/overlays/TextOverlay.cpp +++ b/interface/src/ui/overlays/TextOverlay.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index ec34e90b5c..2d1856e41b 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -70,6 +70,7 @@ void Rig::startAnimation(const QString& url, float fps, float priority, handle = createAnimationHandle(); handle->setURL(url); } + handle->setFade(1.0f); // If you want to fade, use the startAnimationByRole system. handle->setFPS(fps); handle->setPriority(priority); handle->setLoop(loop); @@ -150,7 +151,8 @@ void Rig::stopAnimationByRole(const QString& role) { void Rig::stopAnimation(const QString& url) { foreach (const AnimationHandlePointer& handle, getRunningAnimations()) { if (handle->getURL() == url) { - handle->stop(); + handle->setFade(0.0f); // right away. Will be remove during updateAnimations, without locking + handle->setFadePerSecond(-1.0f); // so that the updateAnimation code notices } } } @@ -472,7 +474,9 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) { float fadeSumSoFar = 0.0f; foreach (const AnimationHandlePointer& handle, _runningAnimations) { handle->setPriority(1.0f); - float normalizedFade = handle->getFade() / fadeTotal; + // if no fadeTotal, everyone's (typically just one running) is starting at zero. In that case, blend equally. + float normalizedFade = (fadeTotal != 0.0f) ? (handle->getFade() / fadeTotal) : (1.0f / _runningAnimations.count()); + assert(normalizedFade != 0.0f); // simulate() will blend each animation result into the result so far, based on the pairwise mix at at each step. // i.e., slerp the 'mix' distance from the result so far towards this iteration's animation result. // The formula here for mix is based on the idea that, at each step: diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 3b7d18f1d8..5e7647263a 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -976,6 +976,8 @@ ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex) { data.extracted.mesh.meshIndex = meshIndex++; QVector materials; QVector textures; + bool isMaterialPerPolygon = false; + foreach (const FBXNode& child, object.children) { if (child.name == "Vertices") { data.vertices = createVec3Vector(getDoubleVector(child)); @@ -1105,8 +1107,16 @@ ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex) { foreach (const FBXNode& subdata, child.children) { if (subdata.name == "Materials") { materials = getIntVector(subdata); + } else if (subdata.name == "MappingInformationType") { + if (subdata.properties.at(0) == "ByPolygon") { + isMaterialPerPolygon = true; + } else { + isMaterialPerPolygon = false; + } } } + + } else if (child.name == "LayerElementTexture") { foreach (const FBXNode& subdata, child.children) { if (subdata.name == "TextureId") { @@ -1116,6 +1126,12 @@ ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex) { } } + + bool isMultiMaterial = false; + if (isMaterialPerPolygon) { + isMultiMaterial = true; + } + // convert the polygons to quads and triangles int polygonIndex = 0; QHash, int> materialTextureParts; @@ -2104,6 +2120,9 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, if (type.contains("diffuse")) { diffuseTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); + } else if (type.contains("transparentcolor")) { // it should be TransparentColor... + // THis is how Maya assign a texture that affect diffuse color AND transparency ? + diffuseTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); } else if (type.contains("bump") || type.contains("normal")) { bumpTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); diff --git a/libraries/gpu/src/gpu/GLBackendPipeline.cpp b/libraries/gpu/src/gpu/GLBackendPipeline.cpp index d0ed1d51bf..8dd5242c3a 100755 --- a/libraries/gpu/src/gpu/GLBackendPipeline.cpp +++ b/libraries/gpu/src/gpu/GLBackendPipeline.cpp @@ -153,7 +153,6 @@ void GLBackend::releaseUniformBuffer(uint32_t slot) { if (buf) { auto* object = Backend::getGPUObject(*buf); if (object) { - GLuint bo = object->_buffer; glBindBufferBase(GL_UNIFORM_BUFFER, slot, 0); // RELEASE (void) CHECK_GL_ERROR(); @@ -222,7 +221,6 @@ void GLBackend::releaseResourceTexture(uint32_t slot) { if (tex) { auto* object = Backend::getGPUObject(*tex); if (object) { - GLuint to = object->_texture; GLuint target = object->_target; glActiveTexture(GL_TEXTURE0 + slot); glBindTexture(target, 0); // RELEASE diff --git a/libraries/networking/src/DataServerAccountInfo.cpp b/libraries/networking/src/DataServerAccountInfo.cpp index a85aa588a8..0628e21574 100644 --- a/libraries/networking/src/DataServerAccountInfo.cpp +++ b/libraries/networking/src/DataServerAccountInfo.cpp @@ -10,11 +10,13 @@ // #include +#include #include #include #include +#include "UUID.h" #include "NetworkLogging.h" #include "DataServerAccountInfo.h" @@ -30,8 +32,7 @@ DataServerAccountInfo::DataServerAccountInfo() : _walletID(), _balance(0), _hasBalance(false), - _privateKey(), - _usernameSignature() + _privateKey() { } @@ -73,9 +74,6 @@ void DataServerAccountInfo::setAccessTokenFromJSON(const QJsonObject& jsonObject void DataServerAccountInfo::setUsername(const QString& username) { if (_username != username) { _username = username; - - // clear our username signature so it has to be re-created - _usernameSignature = QByteArray(); qCDebug(networking) << "Username changed to" << username; } @@ -128,8 +126,8 @@ void DataServerAccountInfo::setProfileInfoFromJSON(const QJsonObject& jsonObject setWalletID(QUuid(user["wallet_id"].toString())); } -const QByteArray& DataServerAccountInfo::getUsernameSignature() { - if (_usernameSignature.isEmpty()) { +QByteArray DataServerAccountInfo::getUsernameSignature(const QUuid& connectionToken) { + if (!_privateKey.isEmpty()) { const char* privateKeyData = _privateKey.constData(); RSA* rsaPrivateKey = d2i_RSAPrivateKey(NULL, @@ -137,36 +135,41 @@ const QByteArray& DataServerAccountInfo::getUsernameSignature() { _privateKey.size()); if (rsaPrivateKey) { QByteArray lowercaseUsername = _username.toLower().toUtf8(); - _usernameSignature.resize(RSA_size(rsaPrivateKey)); + QByteArray usernameWithToken = QCryptographicHash::hash(lowercaseUsername.append(connectionToken.toRfc4122()), + QCryptographicHash::Sha256); - int encryptReturn = RSA_private_encrypt(lowercaseUsername.size(), - reinterpret_cast(lowercaseUsername.constData()), - reinterpret_cast(_usernameSignature.data()), - rsaPrivateKey, RSA_PKCS1_PADDING); + QByteArray usernameSignature(RSA_size(rsaPrivateKey), 0); + unsigned int usernameSignatureSize = 0; - if (encryptReturn == -1) { - qCDebug(networking) << "Error encrypting username signature."; - qCDebug(networking) << "Will re-attempt on next domain-server check in."; - _usernameSignature = QByteArray(); - } + int encryptReturn = RSA_sign(NID_sha256, + reinterpret_cast(usernameWithToken.constData()), + usernameWithToken.size(), + reinterpret_cast(usernameSignature.data()), + &usernameSignatureSize, + rsaPrivateKey); // free the private key RSA struct now that we are done with it RSA_free(rsaPrivateKey); + + if (encryptReturn == -1) { + qCDebug(networking) << "Error encrypting username signature."; + qCDebug(networking) << "Will re-attempt on next domain-server check in."; + } else { + qDebug(networking) << "Returning username" << _username << "signed with connection UUID" << uuidStringWithoutCurlyBraces(connectionToken); + return usernameSignature; + } + } else { qCDebug(networking) << "Could not create RSA struct from QByteArray private key."; qCDebug(networking) << "Will re-attempt on next domain-server check in."; } } - } - - return _usernameSignature; + return QByteArray(); } void DataServerAccountInfo::setPrivateKey(const QByteArray& privateKey) { _privateKey = privateKey; - // clear our username signature so it has to be re-created - _usernameSignature = QByteArray(); } QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) { diff --git a/libraries/networking/src/DataServerAccountInfo.h b/libraries/networking/src/DataServerAccountInfo.h index d7f9d5167a..9b80de5422 100644 --- a/libraries/networking/src/DataServerAccountInfo.h +++ b/libraries/networking/src/DataServerAccountInfo.h @@ -43,7 +43,7 @@ public: const QUuid& getWalletID() const { return _walletID; } void setWalletID(const QUuid& walletID); - const QByteArray& getUsernameSignature(); + QByteArray getUsernameSignature(const QUuid& connectionToken); bool hasPrivateKey() const { return !_privateKey.isEmpty(); } void setPrivateKey(const QByteArray& privateKey); @@ -73,7 +73,7 @@ private: qint64 _balance; bool _hasBalance; QByteArray _privateKey; - QByteArray _usernameSignature; + }; #endif // hifi_DataServerAccountInfo_h diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index afb362053e..8f4b9cc61f 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -29,6 +29,7 @@ DomainHandler::DomainHandler(QObject* parent) : _uuid(), _sockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)), _assignmentUUID(), + _connectionToken(), _iceDomainID(), _iceClientID(), _iceServerSockAddr(), @@ -43,7 +44,8 @@ DomainHandler::DomainHandler(QObject* parent) : void DomainHandler::clearConnectionInfo() { _uuid = QUuid(); - + _connectionToken = QUuid(); + _icePeer.reset(); if (requiresICE()) { diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 6079035f8b..7bb0582914 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -50,9 +50,12 @@ public: unsigned short getPort() const { return _sockAddr.getPort(); } void setPort(quint16 port) { _sockAddr.setPort(port); } + const QUuid& getConnectionToken() const { return _connectionToken; } + void setConnectionToken(const QUuid& connectionToken) { _connectionToken = connectionToken; } + const QUuid& getAssignmentUUID() const { return _assignmentUUID; } void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; } - + const QUuid& getICEDomainID() const { return _iceDomainID; } const QUuid& getICEClientID() const { return _iceClientID; } @@ -114,6 +117,7 @@ private: QString _hostname; HifiSockAddr _sockAddr; QUuid _assignmentUUID; + QUuid _connectionToken; QUuid _iceDomainID; QUuid _iceClientID; HifiSockAddr _iceServerSockAddr; diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index dfa3ef86a5..44b9c0b829 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -94,7 +94,7 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned packetReceiver.registerListener(PacketType::PingReply, this, "processPingReplyPacket"); packetReceiver.registerListener(PacketType::ICEPing, this, "processICEPingPacket"); packetReceiver.registerListener(PacketType::DomainServerAddedNode, this, "processDomainServerAddedNode"); - + packetReceiver.registerListener(PacketType::DomainServerConnectionToken, this, "processDomainServerConnectionTokenPacket"); packetReceiver.registerListener(PacketType::ICEServerPeerInformation, &_domainHandler, "processICEResponsePacket"); packetReceiver.registerListener(PacketType::DomainServerRequireDTLS, &_domainHandler, "processDTLSRequirementPacket"); packetReceiver.registerListener(PacketType::ICEPingReply, &_domainHandler, "processICEPingReplyPacket"); @@ -274,17 +274,23 @@ void NodeList::sendDomainServerCheckIn() { // pack our data to send to the domain-server packetStream << _ownerType << _publicSockAddr << _localSockAddr << _nodeTypesOfInterest.toList(); - + // if this is a connect request, and we can present a username signature, send it along - if (!_domainHandler.isConnected()) { + if (!_domainHandler.isConnected() ) { + DataServerAccountInfo& accountInfo = AccountManager::getInstance().getAccountInfo(); packetStream << accountInfo.getUsername(); - - const QByteArray& usernameSignature = AccountManager::getInstance().getAccountInfo().getUsernameSignature(); - - if (!usernameSignature.isEmpty()) { - qCDebug(networking) << "Including username signature in domain connect request."; - packetStream << usernameSignature; + + // get connection token from the domain-server + const QUuid& connectionToken = _domainHandler.getConnectionToken(); + + if (!connectionToken.isNull()) { + + const QByteArray& usernameSignature = AccountManager::getInstance().getAccountInfo().getUsernameSignature(connectionToken); + + if (!usernameSignature.isEmpty()) { + packetStream << usernameSignature; + } } } @@ -361,6 +367,7 @@ void NodeList::sendDSPathQuery(const QString& newPath) { } } + void NodeList::processDomainServerPathResponse(QSharedPointer packet) { // This is a response to a path query we theoretically made. // In the future we may want to check that this was actually from our DS and for a query we actually made. @@ -450,6 +457,17 @@ void NodeList::pingPunchForDomainServer() { } } + +void NodeList::processDomainServerConnectionTokenPacket(QSharedPointer packet) { + if (_domainHandler.getSockAddr().isNull()) { + // refuse to process this packet if we aren't currently connected to the DS + return; + } + // read in the connection token from the packet, then send domain-server checkin + _domainHandler.setConnectionToken(QUuid::fromRfc4122(packet->read(NUM_BYTES_RFC4122_UUID))); + sendDomainServerCheckIn(); +} + void NodeList::processDomainServerList(QSharedPointer packet) { if (_domainHandler.getSockAddr().isNull()) { // refuse to process this packet if we aren't currently connected to the DS diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index e3f8feeeda..3aae3e3dfc 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -76,6 +76,8 @@ public slots: void processDomainServerAddedNode(QSharedPointer packet); void processDomainServerPathResponse(QSharedPointer packet); + void processDomainServerConnectionTokenPacket(QSharedPointer packet); + void processPingPacket(QSharedPointer packet, SharedNodePointer sendingNode); void processPingReplyPacket(QSharedPointer packet, SharedNodePointer sendingNode); diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 0591ac30fe..f3f222f310 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -26,7 +26,7 @@ const QSet SEQUENCE_NUMBERED_PACKETS = QSet NON_SOURCED_PACKETS = QSet() << StunResponse << CreateAssignment << RequestAssignment - << DomainServerRequireDTLS << DomainConnectRequest + << DomainServerRequireDTLS << DomainConnectRequest << DomainServerConnectionToken << DomainList << DomainConnectionDenied << DomainServerPathQuery << DomainServerPathResponse << DomainServerAddedNode @@ -119,6 +119,7 @@ QString nameForPacketType(PacketType::Value packetType) { PACKET_TYPE_NAME_LOOKUP(ICEPingReply); PACKET_TYPE_NAME_LOOKUP(EntityAdd); PACKET_TYPE_NAME_LOOKUP(EntityEdit); + PACKET_TYPE_NAME_LOOKUP(DomainServerConnectionToken); default: return QString("Type: ") + QString::number((int)packetType); } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 3f3f165e87..46f834db74 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -73,7 +73,8 @@ namespace PacketType { EntityQuery, EntityAdd, EntityErase, - EntityEdit + EntityEdit, + DomainServerConnectionToken }; }; diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index 1143bdf829..85ea0caef0 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -636,8 +636,6 @@ OctreeElement* OctreeElement::getOrCreateChildElementAt(float x, float y, float if (s > halfOurScale) { return this; } - // otherwise, we need to find which of our children we should recurse - glm::vec3 ourCenter = _cube.calcCenter(); int childIndex = getMyChildContainingPoint(glm::vec3(x, y, z)); diff --git a/libraries/render-utils/src/DeferredBufferWrite.slh b/libraries/render-utils/src/DeferredBufferWrite.slh index 91447cbf8b..b9b0198ba5 100755 --- a/libraries/render-utils/src/DeferredBufferWrite.slh +++ b/libraries/render-utils/src/DeferredBufferWrite.slh @@ -21,6 +21,23 @@ uniform float glowIntensity; // the alpha threshold uniform float alphaThreshold; +uniform sampler2D normalFittingMap; + +vec3 bestFitNormal(vec3 normal) { + vec3 absNorm = abs(normal); + float maxNAbs = max(absNorm.z, max(absNorm.x, absNorm.y)); + + vec2 texcoord = (absNorm.z < maxNAbs ? + (absNorm.y < maxNAbs ? absNorm.yz : absNorm.xz) : + absNorm.xy); + texcoord = (texcoord.x < texcoord.y ? texcoord.yx : texcoord.xy); + texcoord.y /= texcoord.x; + vec3 cN = normal / maxNAbs; + float fittingScale = texture(normalFittingMap, texcoord).a; + cN *= fittingScale; + return (cN * 0.5 + 0.5); +} + float evalOpaqueFinalAlpha(float alpha, float mapAlpha) { return mix(alpha * glowIntensity, 1.0 - alpha * glowIntensity, step(mapAlpha, alphaThreshold)); } @@ -33,7 +50,7 @@ void packDeferredFragment(vec3 normal, float alpha, vec3 diffuse, vec3 specular, discard; } _fragColor0 = vec4(diffuse.rgb, alpha); - _fragColor1 = vec4(normal, 0.0) * 0.5 + vec4(0.5, 0.5, 0.5, 1.0); + _fragColor1 = vec4(bestFitNormal(normal), 1.0); _fragColor2 = vec4(specular, shininess / 128.0); } @@ -44,7 +61,7 @@ void packDeferredFragmentLightmap(vec3 normal, float alpha, vec3 diffuse, vec3 s _fragColor0 = vec4(diffuse.rgb, alpha); //_fragColor1 = vec4(normal, 0.0) * 0.5 + vec4(0.5, 0.5, 0.5, 1.0); - _fragColor1 = vec4(normal, 0.0) * 0.5 + vec4(0.5, 0.5, 0.5, 0.5); + _fragColor1 = vec4(bestFitNormal(normal), 0.5); _fragColor2 = vec4(emissive, shininess / 128.0); } diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index aaaa3b6d6a..08ab6e0cf6 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -86,6 +86,7 @@ void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) { _emissiveShader = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PSEmissive)); gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), DeferredLightingEffect::NORMAL_FITTING_MAP_SLOT)); gpu::Shader::makeProgram(*_simpleShader, slotBindings); gpu::Shader::makeProgram(*_emissiveShader, slotBindings); @@ -151,6 +152,8 @@ void DeferredLightingEffect::bindSimpleProgram(gpu::Batch& batch, bool textured, // If it is not textured, bind white texture and keep using textured pipeline batch.setResourceTexture(0, DependencyManager::get()->getWhiteTexture()); } + + batch.setResourceTexture(NORMAL_FITTING_MAP_SLOT, DependencyManager::get()->getNormalFittingTexture()); } void DeferredLightingEffect::renderSolidSphere(gpu::Batch& batch, float radius, int slices, int stacks, const glm::vec4& color) { diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index ba4fbd6d69..62497851d7 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -30,7 +30,8 @@ class DeferredLightingEffect : public Dependency { SINGLETON_DEPENDENCY public: - + static const int NORMAL_FITTING_MAP_SLOT = 10; + void init(AbstractViewStateInterface* viewState); /// Sets up the state necessary to render static untextured geometry with the simple program. diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index f06022e23a..31b030f75a 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -20,7 +20,6 @@ #include #include -#include #include #include "TextureCache.h" @@ -2240,10 +2239,17 @@ bool NetworkMeshPart::isTranslucent() const { return diffuseTexture && diffuseTexture->isTranslucent(); } + +bool NetworkMesh::isPartTranslucent(const FBXMesh& fbxMesh, int partIndex) const { + assert(partIndex >= 0); + assert(partIndex < parts.size()); + return (parts.at(partIndex).isTranslucent() || fbxMesh.parts.at(partIndex).opacity != 1.0f); +} + int NetworkMesh::getTranslucentPartCount(const FBXMesh& fbxMesh) const { int count = 0; for (int i = 0; i < parts.size(); i++) { - if (parts.at(i).isTranslucent() || fbxMesh.parts.at(i).opacity != 1.0f) { + if (isPartTranslucent(fbxMesh, i)) { count++; } } diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 812d12b846..f70eae6380 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -398,6 +398,7 @@ public: QVector parts; int getTranslucentPartCount(const FBXMesh& fbxMesh) const; + bool isPartTranslucent(const FBXMesh& fbxMesh, int partIndex) const; }; #endif // hifi_GeometryCache_h diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index cf0fb67d8c..8839a5b463 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -105,6 +105,8 @@ void Model::RenderPipelineLib::addRenderPipeline(Model::RenderKey key, slotBindings.insert(gpu::Shader::Binding(std::string("specularMap"), 2)); slotBindings.insert(gpu::Shader::Binding(std::string("emissiveMap"), 3)); slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), 4)); + slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), DeferredLightingEffect::NORMAL_FITTING_MAP_SLOT)); + gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vertexShader, pixelShader)); gpu::Shader::makeProgram(*program, slotBindings); @@ -180,6 +182,8 @@ void Model::RenderPipelineLib::initLocations(gpu::ShaderPointer& program, Model: locations.emissiveParams = program->getUniforms().findLocation("emissiveParams"); locations.glowIntensity = program->getUniforms().findLocation("glowIntensity"); + locations.normalFittingMapUnit = program->getTextures().findLocation("normalFittingMap"); + locations.specularTextureUnit = program->getTextures().findLocation("specularMap"); locations.emissiveTextureUnit = program->getTextures().findLocation("emissiveMap"); @@ -1820,11 +1824,10 @@ void Model::segregateMeshGroups() { translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false; } - // Debug... + // Create the render payloads int totalParts = mesh.parts.size(); for (int partIndex = 0; partIndex < totalParts; partIndex++) { - // this is a good place to create our renderPayloads - if (translucentMesh) { + if (networkMesh.isPartTranslucent(mesh, partIndex)) { _transparentRenderItems << std::make_shared(true, this, i, partIndex); } else { _opaqueRenderItems << std::make_shared(false, this, i, partIndex); @@ -1864,6 +1867,10 @@ void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, f const float DEFAULT_GLOW_INTENSITY = 1.0f; // FIXME - glow is removed batch._glUniform1f(locations->glowIntensity, DEFAULT_GLOW_INTENSITY); } + + if ((locations->normalFittingMapUnit > -1)) { + batch.setResourceTexture(locations->normalFittingMapUnit, DependencyManager::get()->getNormalFittingTexture()); + } } bool Model::initWhenReady(render::ScenePointer scene) { diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 09504c7501..9fdb2f3691 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -350,6 +350,7 @@ private: int emissiveTextureUnit; int emissiveParams; int glowIntensity; + int normalFittingMapUnit; int materialBufferUnit; int clusterMatrices; int clusterIndices; diff --git a/libraries/render-utils/src/TextureCache.cpp b/libraries/render-utils/src/TextureCache.cpp index d6a9bf5b36..deeec58f49 100644 --- a/libraries/render-utils/src/TextureCache.cpp +++ b/libraries/render-utils/src/TextureCache.cpp @@ -21,6 +21,7 @@ #include #include #include +#include "PathUtils.h" #include @@ -129,6 +130,14 @@ const gpu::TexturePointer& TextureCache::getBlackTexture() { return _blackTexture; } + +const gpu::TexturePointer& TextureCache::getNormalFittingTexture() { + if (!_normalFittingTexture) { + _normalFittingTexture = getImageTexture(PathUtils::resourcesPath() + "images/normalFittingScale.dds"); + } + return _normalFittingTexture; +} + /// Extra data for creating textures. class TextureExtra { public: @@ -170,6 +179,7 @@ gpu::TexturePointer TextureCache::getImageTexture(const QString& path) { return texture; } + QSharedPointer TextureCache::createResource(const QUrl& url, const QSharedPointer& fallback, bool delayLoad, const void* extra) { const TextureExtra* textureExtra = static_cast(extra); diff --git a/libraries/render-utils/src/TextureCache.h b/libraries/render-utils/src/TextureCache.h index 8f60382e9b..eeb17f07b9 100644 --- a/libraries/render-utils/src/TextureCache.h +++ b/libraries/render-utils/src/TextureCache.h @@ -54,6 +54,9 @@ public: /// Returns the a black texture (useful for a default). const gpu::TexturePointer& getBlackTexture(); + // Returns a map used to compress the normals through a fitting scale algorithm + const gpu::TexturePointer& getNormalFittingTexture(); + /// Returns a texture version of an image file static gpu::TexturePointer getImageTexture(const QString& path); @@ -76,6 +79,7 @@ private: gpu::TexturePointer _grayTexture; gpu::TexturePointer _blueTexture; gpu::TexturePointer _blackTexture; + gpu::TexturePointer _normalFittingTexture; QHash > _dilatableNetworkTextures; }; diff --git a/tests/render-utils/src/main.cpp b/tests/render-utils/src/main.cpp index 162e9425d3..0fa261db8d 100644 --- a/tests/render-utils/src/main.cpp +++ b/tests/render-utils/src/main.cpp @@ -9,37 +9,21 @@ // #include -#include -#include -#include +#include +#include -#include -#include -#include -#include -#include - -#include #include -#include #include #include -#include -#include -#include + +#include +#include +#include +#include #include -#include #include #include -#include -#include -#include - -#include - -#include "gpu/Batch.h" -#include "gpu/Context.h" class RateCounter { std::vector times; @@ -71,7 +55,7 @@ public: float rate() const { if (elapsed() == 0.0f) { - return NAN; + return 0.0f; } return (float) count() / elapsed(); } diff --git a/tests/shaders/src/main.cpp b/tests/shaders/src/main.cpp index f93a7a2302..c514532eac 100644 --- a/tests/shaders/src/main.cpp +++ b/tests/shaders/src/main.cpp @@ -6,39 +6,20 @@ // #include -#include #include -#include -#include -#include -#include -#include -#include - -#include #include -#include -#include #include -#include -#include -#include + +#include #include -#include #include #include #include #include #include -#include - - -#include "gpu/Batch.h" -#include "gpu/Context.h" - #include "../model/Skybox_vert.h" #include "../model/Skybox_frag.h" @@ -159,7 +140,7 @@ public: float rate() const { if (elapsed() == 0.0f) { - return NAN; + return 0.0f; } return (float) count() / elapsed(); } diff --git a/tests/ui/src/main.cpp b/tests/ui/src/main.cpp index 2de2e1e6c6..f4088dd885 100644 --- a/tests/ui/src/main.cpp +++ b/tests/ui/src/main.cpp @@ -68,7 +68,7 @@ public: float rate() const { if (elapsed() == 0.0f) { - return NAN; + return 0.0f; } return (float) count() / elapsed(); }