diff --git a/BUILD_LINUX_CHEATSHEET.md b/BUILD_LINUX_CHEATSHEET.md new file mode 100644 index 0000000000..7d77f5d685 --- /dev/null +++ b/BUILD_LINUX_CHEATSHEET.md @@ -0,0 +1,27 @@ + # this guide is specific to Ubuntu 16.04. + # deb packages of High Fidelity domain server and assignment client are stored on debian.highfidelity.com +sudo su - +apt-get -y update +apt-get install -y software-properties-common +apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 15FF1AAE +add-apt-repository "deb http://debian.highfidelity.com stable main" +apt-get -y update +apt-get install -y hifi-domain-server +apt-get install -y hifi-assignment-client + + # When installing master/dev builds, the packages are slightly different and you just need to change the last 2 steps to: +apt-get install -y hifi-dev-domain-server +apt-get install -y hifi-dev-assignment-client + + # domain server and assignment clients should already be running. The processes are controlled via: +systemctl start hifi-domain-server +systemctl stop hifi-domain-server + + # Once the machine is setup and processes are running you should ensure that your firewall exposes port 40100 on TCP and all UDP ports. This will get your domain up and running and you could connect to it (for now) by using High Fidelity Interface and typing in the IP for the place name. (further customizations can be done via http://IPAddress:40100). + + # The server always depends on both hifi-domain-server and hifi-assignment-client running at the same time. + # As an additional step, you should ensure that your packages are automatically updated when a new version goes out. You could, for example, set the automatic update checks to happen every hour (though this could potentially result in the domain being unreachable for a whole hour by new clients when they are released - adjust the update checks accordingly). +To do this you can modify /etc/crontab by adding the following lines +0 */1 * * * root apt-get update +1 */1 * * * root apt-get install --only-upgrade -y hifi-domain-server +2 */1 * * * root apt-get install --only-upgrade -y hifi-assignment-client diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 47a81ba1fe..fb4b65726a 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -214,7 +214,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) uint64_t getTimestamp() const override { return _lastEncodeTime; } - const AvatarSharedPointer& getAvatar() const { return _avatar; } + AvatarSharedPointer getAvatar() const { return _avatar; } private: AvatarSharedPointer _avatar; @@ -326,7 +326,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) int remainingAvatars = (int)sortedAvatars.size(); while (!sortedAvatars.empty()) { - const auto& avatarData = sortedAvatars.top().getAvatar(); + const auto avatarData = sortedAvatars.top().getAvatar(); sortedAvatars.pop(); remainingAvatars--; diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 6b7432b8b2..4de09c1bf3 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -33,6 +33,7 @@ Rectangle { property string itemName; property string itemId; property string itemHref; + property string itemAuthor; property double balanceAfterPurchase; property bool alreadyOwned: false; property int itemPrice: -1; @@ -81,12 +82,12 @@ Rectangle { if (result.status !== 'success') { failureErrorText.text = result.message; root.activeView = "checkoutFailure"; - UserActivityLogger.commercePurchaseFailure(root.itemId, root.itemPrice, !root.alreadyOwned, result.message); + UserActivityLogger.commercePurchaseFailure(root.itemId, root.itemAuthor, root.itemPrice, !root.alreadyOwned, result.message); } else { root.itemHref = result.data.download_url; root.isWearable = result.data.categories.indexOf("Wearables") > -1; root.activeView = "checkoutSuccess"; - UserActivityLogger.commercePurchaseSuccess(root.itemId, root.itemPrice, !root.alreadyOwned); + UserActivityLogger.commercePurchaseSuccess(root.itemId, root.itemAuthor, root.itemPrice, !root.alreadyOwned); } } @@ -410,7 +411,8 @@ Rectangle { Rectangle { id: buyTextContainer; visible: buyText.text !== ""; - anchors.top: parent.top; + anchors.top: cancelPurchaseButton.bottom; + anchors.topMargin: 16; anchors.left: parent.left; anchors.right: parent.right; height: buyText.height + 30; @@ -465,8 +467,8 @@ Rectangle { enabled: (root.balanceAfterPurchase >= 0 && purchasesReceived && balanceReceived) || !itemIsJson; color: hifi.buttons.blue; colorScheme: hifi.colorSchemes.light; - anchors.top: buyTextContainer.visible ? buyTextContainer.bottom : checkoutActionButtonsContainer.top; - anchors.topMargin: buyTextContainer.visible ? 12 : 16; + anchors.top: checkoutActionButtonsContainer.top; + anchors.topMargin: 16; height: 40; anchors.left: parent.left; anchors.right: parent.right; @@ -879,6 +881,7 @@ Rectangle { root.itemPrice = message.params.itemPrice; itemHref = message.params.itemHref; referrer = message.params.referrer; + itemAuthor = message.params.itemAuthor; setBuyText(); break; default: @@ -926,11 +929,11 @@ Rectangle { buyText.text = ""; } } else { - buyText.text = "This free item will not be added to your Purchases. Non-entities can't yet be purchased for HFC."; - buyTextContainer.color = "#FFD6AD"; - buyTextContainer.border.color = "#FAC07D"; - buyGlyph.text = hifi.glyphs.alert; - buyGlyph.size = 46; + buyText.text = 'This type of item cannot currently be certified, so it will not show up in "My Purchases". You can access it again for free from the Marketplace.'; + buyTextContainer.color = hifi.colors.white; + buyTextContainer.border.color = hifi.colors.white; + buyGlyph.text = ""; + buyGlyph.size = 0; } } diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 8a294182bd..93caef555f 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -150,7 +150,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); } float getRadius() const override { return std::static_pointer_cast(_avatar)->getBoundingRadius(); } uint64_t getTimestamp() const override { return std::static_pointer_cast(_avatar)->getLastRenderUpdateTime(); } - const AvatarSharedPointer& getAvatar() const { return _avatar; } + AvatarSharedPointer getAvatar() const { return _avatar; } private: AvatarSharedPointer _avatar; }; @@ -185,7 +185,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { render::Transaction transaction; while (!sortedAvatars.empty()) { const SortableAvatar& sortData = sortedAvatars.top(); - const auto& avatar = std::static_pointer_cast(sortData.getAvatar()); + const auto avatar = std::static_pointer_cast(sortData.getAvatar()); bool ignoring = DependencyManager::get()->isPersonalMutingNode(avatar->getID()); if (ignoring) { @@ -239,7 +239,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { sortedAvatars.pop(); while (inView && !sortedAvatars.empty()) { const SortableAvatar& newSortData = sortedAvatars.top(); - const auto& newAvatar = std::static_pointer_cast(newSortData.getAvatar()); + const auto newAvatar = std::static_pointer_cast(newSortData.getAvatar()); inView = newSortData.getPriority() > OUT_OF_VIEW_THRESHOLD; if (inView && newAvatar->hasNewJointData()) { numAVatarsNotUpdated++; diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 3257a634c7..d7d36dabf6 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -61,7 +61,7 @@ void Ledger::send(const QString& endpoint, const QString& success, const QString void Ledger::signedSend(const QString& propertyName, const QByteArray& text, const QString& key, const QString& endpoint, const QString& success, const QString& fail, const bool controlled_failure) { auto wallet = DependencyManager::get(); - QString signature = key.isEmpty() ? "" : wallet->signWithKey(text, key); + QString signature = wallet->signWithKey(text, key); QJsonObject request; request[propertyName] = QString(text); if (!controlled_failure) { diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 00941d6c50..c3c91e82a8 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -548,13 +548,16 @@ QStringList Wallet::listPublicKeys() { // the horror of code pages and so on (changing the bytes) by just returning a base64 // encoded string representing the signature (suitable for http, etc...) QString Wallet::signWithKey(const QByteArray& text, const QString& key) { - qCInfo(commerce) << "Signing text" << text << "with key" << key; EC_KEY* ecPrivateKey = NULL; + + auto keyFilePathString = keyFilePath().toStdString(); if ((ecPrivateKey = readPrivateKey(keyFilePath().toStdString().c_str()))) { unsigned char* sig = new unsigned char[ECDSA_size(ecPrivateKey)]; unsigned int signatureBytes = 0; + qCInfo(commerce) << "Hashing and signing plaintext" << text << "with key at address" << ecPrivateKey; + QByteArray hashedPlaintext = QCryptographicHash::hash(text, QCryptographicHash::Sha256); @@ -747,12 +750,10 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack } EC_KEY_free(ec); - QByteArray ba = sig.toLocal8Bit(); - const char *sigChar = ba.data(); QByteArray textByteArray; if (status > -1) { - textByteArray = QByteArray(sigChar, (int) strlen(sigChar)); + textByteArray = sig.toUtf8(); } textByteArraySize = textByteArray.size(); int certIDSize = certID.size(); diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index ba8bf8cbef..67fa94f7e5 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -24,7 +24,6 @@ ModelOverlay::ModelOverlay() : _model(std::make_shared(nullptr, this)), _modelTextures(QVariantMap()) { - _model->init(); _model->setLoadingPriority(_loadPriority); _isLoaded = false; } @@ -38,7 +37,6 @@ ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) : _scaleToFit(modelOverlay->_scaleToFit), _loadPriority(modelOverlay->_loadPriority) { - _model->init(); _model->setLoadingPriority(_loadPriority); if (_url.isValid()) { _updateModel = true; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index bb7f141cd9..c532e7659f 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -138,7 +138,6 @@ Avatar::~Avatar() { void Avatar::init() { getHead()->init(); - _skeletonModel->init(); _initialized = true; } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 8c3b56d89e..bf711b94c2 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -349,7 +349,7 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene float getRadius() const override { return 0.5f * _renderer->getEntity()->getQueryAACube().getScale(); } uint64_t getTimestamp() const override { return _renderer->getUpdateTime(); } - const EntityRendererPointer& getRenderer() const { return _renderer; } + EntityRendererPointer getRenderer() const { return _renderer; } private: EntityRendererPointer _renderer; }; @@ -382,7 +382,7 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene std::unordered_map::iterator itr; size_t numSorted = sortedRenderables.size(); while (!sortedRenderables.empty() && usecTimestampNow() < expiry) { - const EntityRendererPointer& renderable = sortedRenderables.top().getRenderer(); + const auto renderable = sortedRenderables.top().getRenderer(); renderable->updateInScene(scene, transaction); _renderablesToUpdate.erase(renderable->getEntity()->getID()); sortedRenderables.pop(); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index bc99522d0f..e40e9a2f0c 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1209,7 +1209,6 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce connect(model.get(), &Model::requestRenderUpdate, this, &ModelEntityRenderer::requestRenderUpdate); connect(entity.get(), &RenderableModelEntityItem::requestCollisionGeometryUpdate, this, &ModelEntityRenderer::flagForCollisionGeometryUpdate); model->setLoadingPriority(EntityTreeRenderer::getEntityLoadingPriority(*entity)); - model->init(); entity->setModel(model); withWriteLock([&] { _model = model; }); } diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index fbf85fa8c2..21764dff7f 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -303,6 +303,7 @@ void PolyLineEntityRenderer::doRender(RenderArgs* args) { batch.setInputBuffer(0, _verticesBuffer, 0, sizeof(Vertex)); #ifndef POLYLINE_ENTITY_USE_FADE_EFFECT + // glColor4f must be called after setInputFormat if it must be taken into account if (_isFading) { batch._glColor4f(1.0f, 1.0f, 1.0f, Interpolate::calculateFadeRatio(_fadeStartTime)); } else { diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 3524709395..b00ad0259e 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -137,11 +137,10 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { }); if (proceduralRender) { - batch._glColor4f(outColor.r, outColor.g, outColor.b, outColor.a); if (render::ShapeKey(args->_globalShapeKey).isWireframe()) { - geometryCache->renderWireShape(batch, geometryShape); + geometryCache->renderWireShape(batch, geometryShape, outColor); } else { - geometryCache->renderShape(batch, geometryShape); + geometryCache->renderShape(batch, geometryShape, outColor); } } else { // FIXME, support instanced multi-shape rendering using multidraw indirect diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 16e2c4c789..fb4c50cfc2 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -190,7 +190,6 @@ void WebEntityRenderer::doRender(RenderArgs* args) { }); batch.setResourceTexture(0, _texture); float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; - batch._glColor4f(1.0f, 1.0f, 1.0f, fadeRatio); DependencyManager::get()->bindWebBrowserProgram(batch, fadeRatio < OPAQUE_ALPHA_THRESHOLD); DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, fadeRatio), _geometryId); diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 678ddfcea5..13ebd9ef9f 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -2530,7 +2530,8 @@ bool EntityItemProperties::verifySignature(const QString& publicKey, const QByte return false; } - const unsigned char* key = reinterpret_cast(publicKey.toUtf8().constData()); + auto keyByteArray = publicKey.toUtf8(); + auto key = keyByteArray.constData(); int keyLength = publicKey.length(); BIO *bio = BIO_new_mem_buf((void*)key, keyLength); @@ -2548,19 +2549,23 @@ bool EntityItemProperties::verifySignature(const QString& publicKey, const QByte // ECSDA verification prototype: note that type is currently ignored // int ECDSA_verify(int type, const unsigned char *dgst, int dgstlen, // const unsigned char *sig, int siglen, EC_KEY *eckey); - bool answer = ECDSA_verify(0, + int answer = ECDSA_verify(0, digest, digestLength, signature, signatureLength, ec); long error = ERR_get_error(); - if (error != 0) { - const char* error_str = ERR_error_string(error, NULL); - qCWarning(entities) << "ERROR while verifying signature! EC error:" << error_str + if (error != 0 || answer == -1) { + qCWarning(entities) << "ERROR while verifying signature!" << "\nKey:" << publicKey << "\nutf8 Key Length:" << keyLength << "\nDigest:" << digest << "\nDigest Length:" << digestLength << "\nSignature:" << signature << "\nSignature Length:" << signatureLength; + while (error != 0) { + const char* error_str = ERR_error_string(error, NULL); + qCWarning(entities) << "EC error:" << error_str; + error = ERR_get_error(); + } } EC_KEY_free(ec); if (bio) { @@ -2569,7 +2574,7 @@ bool EntityItemProperties::verifySignature(const QString& publicKey, const QByte if (evp_key) { EVP_PKEY_free(evp_key); } - return answer; + return (answer == 1); } else { if (bio) { BIO_free(bio); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index b5765bb44b..8f780355db 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1189,13 +1189,15 @@ bool EntityTree::verifyNonce(const QString& certID, const QString& nonce, Entity key = sent.second; } - QString annotatedKey = "-----BEGIN PUBLIC KEY-----\n" + key.insert(64, "\n") + "\n-----END PUBLIC KEY-----"; - bool verificationSuccess = EntityItemProperties::verifySignature(annotatedKey.toUtf8(), actualNonce.toUtf8(), nonce.toUtf8()); + QString annotatedKey = "-----BEGIN PUBLIC KEY-----\n" + key.insert(64, "\n") + "\n-----END PUBLIC KEY-----\n"; + QByteArray hashedActualNonce = QCryptographicHash::hash(QByteArray(actualNonce.toUtf8()), QCryptographicHash::Sha256); + bool verificationSuccess = EntityItemProperties::verifySignature(annotatedKey.toUtf8(), hashedActualNonce, QByteArray::fromBase64(nonce.toUtf8())); if (verificationSuccess) { qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded."; } else { - qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed for nonce" << actualNonce << "key" << key << "signature" << nonce; + qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed. Actual nonce:" << actualNonce << + "\nHashed actual nonce (digest):" << hashedActualNonce << "\nSent nonce (signature)" << nonce << "\nKey" << key; } return verificationSuccess; diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index f93d430152..2192f5f45b 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -605,6 +605,10 @@ void GLBackend::do_glColor4f(const Batch& batch, size_t paramOffset) { if (_input._colorAttribute != newColor) { _input._colorAttribute = newColor; glVertexAttrib4fv(gpu::Stream::COLOR, &_input._colorAttribute.r); + // Color has been changed and is not white. To prevent colors from bleeding + // between different objects, we need to set the _hadColorAttribute flag + // as if a previous render call had potential colors + _input._hadColorAttribute = (newColor != glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)); } (void)CHECK_GL_ERROR(); } diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.h b/libraries/gpu-gl/src/gpu/gl/GLBackend.h index 1908db614d..5558d3ada1 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.h @@ -253,6 +253,7 @@ protected: struct InputStageState { bool _invalidFormat { true }; + bool _hadColorAttribute{ true }; Stream::FormatPointer _format; std::string _formatKey; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendInput.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendInput.cpp index e8ebcbe05c..42bd56e6e4 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendInput.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendInput.cpp @@ -62,6 +62,8 @@ void GL41Backend::updateInput() { // now we need to bind the buffers and assign the attrib pointers if (_input._format) { + bool hasColorAttribute{ false }; + const Buffers& buffers = _input._buffers; const Offsets& offsets = _input._bufferOffsets; const Offsets& strides = _input._bufferStrides; @@ -98,6 +100,8 @@ void GL41Backend::updateInput() { uintptr_t pointer = (uintptr_t)(attrib._offset + offsets[bufferNum]); GLboolean isNormalized = attrib._element.isNormalized(); + hasColorAttribute = hasColorAttribute || (slot == Stream::COLOR); + for (size_t locNum = 0; locNum < locationCount; ++locNum) { if (attrib._element.isInteger()) { glVertexAttribIPointer(slot + (GLuint)locNum, count, type, stride, @@ -117,6 +121,15 @@ void GL41Backend::updateInput() { } } } + + if (_input._hadColorAttribute && !hasColorAttribute) { + // The previous input stage had a color attribute but this one doesn't so reset + // color to pure white. + const auto white = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); + glVertexAttrib4fv(Stream::COLOR, &white.r); + _input._colorAttribute = white; + } + _input._hadColorAttribute = hasColorAttribute; } // everything format related should be in sync now _input._invalidFormat = false; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendInput.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendInput.cpp index ece62e15f1..4a43fc988c 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendInput.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendInput.cpp @@ -32,6 +32,8 @@ void GL45Backend::updateInput() { // Assign the vertex format required if (_input._format) { + bool hasColorAttribute{ false }; + _input._attribBindingBuffers.reset(); const Stream::Format::AttributeMap& attributes = _input._format->getAttributes(); @@ -54,6 +56,9 @@ void GL45Backend::updateInput() { GLboolean isNormalized = attrib._element.isNormalized(); GLenum perLocationSize = attrib._element.getLocationSize(); + + hasColorAttribute = hasColorAttribute || (slot == Stream::COLOR); + for (GLuint locNum = 0; locNum < locationCount; ++locNum) { GLuint attriNum = (GLuint)(slot + locNum); newActivation.set(attriNum); @@ -84,6 +89,15 @@ void GL45Backend::updateInput() { glVertexBindingDivisor(bufferChannelNum, frequency); #endif } + + if (_input._hadColorAttribute && !hasColorAttribute) { + // The previous input stage had a color attribute but this one doesn't so reset + // color to pure white. + const auto white = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); + glVertexAttrib4fv(Stream::COLOR, &white.r); + _input._colorAttribute = white; + } + _input._hadColorAttribute = hasColorAttribute; } // Manage Activation what was and what is expected now diff --git a/libraries/networking/src/SentPacketHistory.cpp b/libraries/networking/src/SentPacketHistory.cpp index fbb7eff41a..3bd6d4cf03 100644 --- a/libraries/networking/src/SentPacketHistory.cpp +++ b/libraries/networking/src/SentPacketHistory.cpp @@ -24,8 +24,7 @@ SentPacketHistory::SentPacketHistory(int size) } -void SentPacketHistory::packetSent(uint16_t sequenceNumber, const NLPacket& packet) { - +void SentPacketHistory::untrackedPacketSent(uint16_t sequenceNumber) { // check if given seq number has the expected value. if not, something's wrong with // the code calling this function uint16_t expectedSequenceNumber = _newestSequenceNumber + (uint16_t)1; @@ -34,6 +33,10 @@ void SentPacketHistory::packetSent(uint16_t sequenceNumber, const NLPacket& pack << "Expected:" << expectedSequenceNumber << "Actual:" << sequenceNumber; } _newestSequenceNumber = sequenceNumber; +} + +void SentPacketHistory::packetSent(uint16_t sequenceNumber, const NLPacket& packet) { + untrackedPacketSent(sequenceNumber); QWriteLocker locker(&_packetsLock); _sentPackets.insert(NLPacket::createCopy(packet)); diff --git a/libraries/networking/src/SentPacketHistory.h b/libraries/networking/src/SentPacketHistory.h index 72150658e3..dc92d38b25 100644 --- a/libraries/networking/src/SentPacketHistory.h +++ b/libraries/networking/src/SentPacketHistory.h @@ -27,6 +27,8 @@ class SentPacketHistory { public: SentPacketHistory(int size = MAX_REASONABLE_SEQUENCE_GAP); + void untrackedPacketSent(uint16_t sequenceNumber); + void packetSent(uint16_t sequenceNumber, const NLPacket& packet); const NLPacket* getPacket(uint16_t sequenceNumber) const; diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp index aec6df4f14..c63170de75 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp @@ -89,17 +89,19 @@ void UserActivityLoggerScriptingInterface::doLogAction(QString action, QJsonObje Q_ARG(QJsonObject, details)); } -void UserActivityLoggerScriptingInterface::commercePurchaseSuccess(QString marketplaceID, int cost, bool firstPurchaseOfThisItem) { +void UserActivityLoggerScriptingInterface::commercePurchaseSuccess(QString marketplaceID, QString contentCreator, int cost, bool firstPurchaseOfThisItem) { QJsonObject payload; payload["marketplaceID"] = marketplaceID; + payload["contentCreator"] = contentCreator; payload["cost"] = cost; payload["firstPurchaseOfThisItem"] = firstPurchaseOfThisItem; doLogAction("commercePurchaseSuccess", payload); } -void UserActivityLoggerScriptingInterface::commercePurchaseFailure(QString marketplaceID, int cost, bool firstPurchaseOfThisItem, QString errorDetails) { +void UserActivityLoggerScriptingInterface::commercePurchaseFailure(QString marketplaceID, QString contentCreator, int cost, bool firstPurchaseOfThisItem, QString errorDetails) { QJsonObject payload; payload["marketplaceID"] = marketplaceID; + payload["contentCreator"] = contentCreator; payload["cost"] = cost; payload["firstPurchaseOfThisItem"] = firstPurchaseOfThisItem; payload["errorDetails"] = errorDetails; diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.h b/libraries/networking/src/UserActivityLoggerScriptingInterface.h index 0e08b050d7..71d411056d 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.h +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.h @@ -33,8 +33,8 @@ public: Q_INVOKABLE void bubbleToggled(bool newValue); Q_INVOKABLE void bubbleActivated(); Q_INVOKABLE void logAction(QString action, QVariantMap details = QVariantMap{}); - Q_INVOKABLE void commercePurchaseSuccess(QString marketplaceID, int cost, bool firstPurchaseOfThisItem); - Q_INVOKABLE void commercePurchaseFailure(QString marketplaceID, int cost, bool firstPurchaseOfThisItem, QString errorDetails); + Q_INVOKABLE void commercePurchaseSuccess(QString marketplaceID, QString contentCreator, int cost, bool firstPurchaseOfThisItem); + Q_INVOKABLE void commercePurchaseFailure(QString marketplaceID, QString contentCreator, int cost, bool firstPurchaseOfThisItem, QString errorDetails); Q_INVOKABLE void commerceEntityRezzed(QString marketplaceID, QString source, QString type); Q_INVOKABLE void commerceWalletSetupStarted(int timestamp, QString setupAttemptID, int setupFlowVersion, QString referrer, QString currentDomain); Q_INVOKABLE void commerceWalletSetupProgress(int timestamp, QString setupAttemptID, int secondsElapsed, int currentStepNumber, QString currentStepName); diff --git a/libraries/networking/src/udt/Connection.cpp b/libraries/networking/src/udt/Connection.cpp index 2f57523f79..77ed589e0b 100644 --- a/libraries/networking/src/udt/Connection.cpp +++ b/libraries/networking/src/udt/Connection.cpp @@ -191,6 +191,8 @@ void Connection::queueReceivedMessagePacket(std::unique_ptr packet) { pendingMessage.enqueuePacket(std::move(packet)); + bool processedLastOrOnly = false; + while (pendingMessage.hasAvailablePackets()) { auto packet = pendingMessage.removeNextPacket(); @@ -201,9 +203,13 @@ void Connection::queueReceivedMessagePacket(std::unique_ptr packet) { // if this was the last or only packet, then we can remove the pending message from our hash if (packetPosition == Packet::PacketPosition::LAST || packetPosition == Packet::PacketPosition::ONLY) { - _pendingReceivedMessages.erase(messageNumber); + processedLastOrOnly = true; } } + + if (processedLastOrOnly) { + _pendingReceivedMessages.erase(messageNumber); + } } void Connection::sync() { diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 62354da11a..0a75e8c31b 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -30,7 +30,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return static_cast(EntityVersion::StaticCertJsonVersionOne); + return static_cast(EntityVersion::OwnershipChallengeFix); case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::ConnectionIdentifier); diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 618ac2de0c..85f321a198 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -197,10 +197,11 @@ uint qHash(const PacketType& key, uint seed); QDebug operator<<(QDebug debug, const PacketType& type); enum class EntityVersion : PacketVersion { - StrokeColorProperty = 77, + StrokeColorProperty = 0, HasDynamicOwnershipTests, HazeEffect, - StaticCertJsonVersionOne + StaticCertJsonVersionOne, + OwnershipChallengeFix, }; enum class EntityScriptCallMethodVersion : PacketVersion { diff --git a/libraries/octree/src/OctreeEditPacketSender.cpp b/libraries/octree/src/OctreeEditPacketSender.cpp index 9cb383df41..7e46831faa 100644 --- a/libraries/octree/src/OctreeEditPacketSender.cpp +++ b/libraries/octree/src/OctreeEditPacketSender.cpp @@ -291,6 +291,9 @@ void OctreeEditPacketSender::queueOctreeEditMessage(PacketType type, QByteArray& // release the new packet releaseQueuedPacketList(nodeUUID, std::move(newPacket)); + // tell the sent packet history that we used a sequence number for an untracked packet + auto& sentPacketHistory = _sentPacketHistories[nodeUUID]; + sentPacketHistory.untrackedPacketSent(sequence); } else { std::unique_ptr& bufferedPacket = _pendingEditPackets[nodeUUID].first; //only a NLPacket for now diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp index 3bb2aa2ef9..396cd13508 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp @@ -40,17 +40,7 @@ void CauterizedMeshPartPayload::updateTransformForCauterizedMesh(const Transform void CauterizedMeshPartPayload::bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { // Still relying on the raw data from the model - bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE && renderMode != RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE); - if (useCauterizedMesh) { - ModelPointer model = _model.lock(); - if (model) { - CauterizedModel* skeleton = static_cast(model.get()); - useCauterizedMesh = useCauterizedMesh && skeleton->getEnableCauterization(); - } else { - useCauterizedMesh = false; - } - } - + bool useCauterizedMesh = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE && renderMode != RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) && _enableCauterization; if (useCauterizedMesh) { if (_cauterizedClusterBuffer) { batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, _cauterizedClusterBuffer); diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.h b/libraries/render-utils/src/CauterizedMeshPartPayload.h index 1c98f5abf3..44eddc6e31 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.h +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.h @@ -21,9 +21,12 @@ public: void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override; + void setEnableCauterization(bool enableCauterization) { _enableCauterization = enableCauterization; } + private: gpu::BufferPointer _cauterizedClusterBuffer; Transform _cauterizedTransform; + bool _enableCauterization { false }; }; #endif // hifi_CauterizedMeshPartPayload_h diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index c437a8c556..dbb82ab638 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -178,6 +178,12 @@ void CauterizedModel::updateRenderItems() { modelTransform.setTranslation(self->getTranslation()); modelTransform.setRotation(self->getRotation()); + bool isWireframe = self->isWireframe(); + bool isVisible = self->isVisible(); + bool isLayeredInFront = self->isLayeredInFront(); + bool isLayeredInHUD = self->isLayeredInHUD(); + bool enableCauterization = self->getEnableCauterization(); + render::Transaction transaction; for (int i = 0; i < (int)self->_modelMeshRenderItemIDs.size(); i++) { @@ -186,7 +192,10 @@ void CauterizedModel::updateRenderItems() { auto clusterMatrices(self->getMeshState(meshIndex).clusterMatrices); auto clusterMatricesCauterized(self->getCauterizeMeshState(meshIndex).clusterMatrices); - transaction.updateItem(itemID, [modelTransform, clusterMatrices, clusterMatricesCauterized](CauterizedMeshPartPayload& data) { + bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex); + + transaction.updateItem(itemID, [modelTransform, clusterMatrices, clusterMatricesCauterized, invalidatePayloadShapeKey, + isWireframe, isVisible, isLayeredInFront, isLayeredInHUD, enableCauterization](CauterizedMeshPartPayload& data) { data.updateClusterBuffer(clusterMatrices, clusterMatricesCauterized); Transform renderTransform = modelTransform; @@ -200,6 +209,11 @@ void CauterizedModel::updateRenderItems() { renderTransform = modelTransform.worldTransform(Transform(clusterMatricesCauterized[0])); } data.updateTransformForCauterizedMesh(renderTransform); + + data.setEnableCauterization(enableCauterization); + data.setKey(isVisible, isLayeredInFront || isLayeredInHUD); + data.setLayer(isLayeredInFront, isLayeredInHUD); + data.setShapeKey(invalidatePayloadShapeKey, isWireframe); }); } diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 76c354bdf8..2616d08600 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -760,6 +760,20 @@ void GeometryCache::renderWireShape(gpu::Batch& batch, Shape shape) { _shapes[shape].drawWire(batch); } +void GeometryCache::renderShape(gpu::Batch& batch, Shape shape, const glm::vec4& color) { + batch.setInputFormat(getSolidStreamFormat()); + // Color must be set after input format + batch._glColor4f(color.r, color.g, color.b, color.a); + _shapes[shape].draw(batch); +} + +void GeometryCache::renderWireShape(gpu::Batch& batch, Shape shape, const glm::vec4& color) { + batch.setInputFormat(getSolidStreamFormat()); + // Color must be set after input format + batch._glColor4f(color.r, color.g, color.b, color.a); + _shapes[shape].drawWire(batch); +} + void setupBatchInstance(gpu::Batch& batch, gpu::BufferPointer colorBuffer) { gpu::BufferView colorView(colorBuffer, COLOR_ELEMENT); batch.setInputBuffer(gpu::Stream::COLOR, colorView); @@ -811,6 +825,14 @@ void GeometryCache::renderWireCube(gpu::Batch& batch) { renderWireShape(batch, Cube); } +void GeometryCache::renderCube(gpu::Batch& batch, const glm::vec4& color) { + renderShape(batch, Cube, color); +} + +void GeometryCache::renderWireCube(gpu::Batch& batch, const glm::vec4& color) { + renderWireShape(batch, Cube, color); +} + void GeometryCache::renderSphere(gpu::Batch& batch) { renderShape(batch, Sphere); } @@ -819,6 +841,14 @@ void GeometryCache::renderWireSphere(gpu::Batch& batch) { renderWireShape(batch, Sphere); } +void GeometryCache::renderSphere(gpu::Batch& batch, const glm::vec4& color) { + renderShape(batch, Sphere, color); +} + +void GeometryCache::renderWireSphere(gpu::Batch& batch, const glm::vec4& color) { + renderWireShape(batch, Sphere, color); +} + void GeometryCache::renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner, int majorRows, int majorCols, float majorEdge, int minorRows, int minorCols, float minorEdge, diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index cd8c43f1df..0585cc9e55 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -251,14 +251,20 @@ public: // Dynamic geometry void renderShape(gpu::Batch& batch, Shape shape); void renderWireShape(gpu::Batch& batch, Shape shape); + void renderShape(gpu::Batch& batch, Shape shape, const glm::vec4& color); + void renderWireShape(gpu::Batch& batch, Shape shape, const glm::vec4& color); size_t getShapeTriangleCount(Shape shape); void renderCube(gpu::Batch& batch); void renderWireCube(gpu::Batch& batch); + void renderCube(gpu::Batch& batch, const glm::vec4& color); + void renderWireCube(gpu::Batch& batch, const glm::vec4& color); size_t getCubeTriangleCount(); void renderSphere(gpu::Batch& batch); void renderWireSphere(gpu::Batch& batch); + void renderSphere(gpu::Batch& batch, const glm::vec4& color); + void renderWireSphere(gpu::Batch& batch, const glm::vec4& color); size_t getSphereTriangleCount(); void renderGrid(gpu::Batch& batch, const glm::vec2& minCorner, const glm::vec2& maxCorner, diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 1ea3e1a705..2a59c7d3c5 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -122,11 +122,6 @@ void MeshPartPayload::bindMesh(gpu::Batch& batch) { batch.setInputFormat((_drawMesh->getVertexFormat())); batch.setInputStream(0, _drawMesh->getVertexStream()); - - // TODO: Get rid of that extra call - if (!_hasColorAttrib) { - batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - } } void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations, bool enableTextures) const { @@ -325,7 +320,7 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in _shapeID(shapeIndex) { assert(model && model->isLoaded()); - _model = model; + _blendedVertexBuffer = model->_blendedVertexBuffers[_meshIndex]; auto& modelMesh = model->getGeometry()->getMeshes().at(_meshIndex); const Model::MeshState& state = model->getMeshState(_meshIndex); @@ -339,13 +334,10 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in } updateTransformForSkinnedMesh(renderTransform, transform); - initCache(); + initCache(model); } -void ModelMeshPartPayload::initCache() { - ModelPointer model = _model.lock(); - assert(model && model->isLoaded()); - +void ModelMeshPartPayload::initCache(const ModelPointer& model) { if (_drawMesh) { auto vertexFormat = _drawMesh->getVertexFormat(); _hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR); @@ -355,6 +347,7 @@ void ModelMeshPartPayload::initCache() { const FBXMesh& mesh = geometry.meshes.at(_meshIndex); _isBlendShaped = !mesh.blendshapes.isEmpty(); + _hasTangents = !mesh.tangents.isEmpty(); } auto networkMaterial = model->getGeometry()->getShapeMaterial(_shapeID); @@ -388,94 +381,70 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& render _worldBound.transform(boundTransform); } -ItemKey ModelMeshPartPayload::getKey() const { +void ModelMeshPartPayload::setKey(bool isVisible, bool isLayered) { ItemKey::Builder builder; builder.withTypeShape(); - ModelPointer model = _model.lock(); - if (model) { - if (!model->isVisible()) { - builder.withInvisible(); - } + if (!isVisible) { + builder.withInvisible(); + } - if (model->isLayeredInFront() || model->isLayeredInHUD()) { - builder.withLayered(); - } + if (isLayered) { + builder.withLayered(); + } - if (_isBlendShaped || _isSkinned) { - builder.withDeformed(); - } + if (_isBlendShaped || _isSkinned) { + builder.withDeformed(); + } - if (_drawMaterial) { - auto matKey = _drawMaterial->getKey(); - if (matKey.isTranslucent()) { - builder.withTransparent(); - } + if (_drawMaterial) { + auto matKey = _drawMaterial->getKey(); + if (matKey.isTranslucent()) { + builder.withTransparent(); } } - return builder.build(); + + _itemKey = builder.build(); +} + +ItemKey ModelMeshPartPayload::getKey() const { + return _itemKey; +} + +void ModelMeshPartPayload::setLayer(bool isLayeredInFront, bool isLayeredInHUD) { + if (isLayeredInFront) { + _layer = Item::LAYER_3D_FRONT; + } else if (isLayeredInHUD) { + _layer = Item::LAYER_3D_HUD; + } else { + _layer = Item::LAYER_3D; + } } int ModelMeshPartPayload::getLayer() const { - ModelPointer model = _model.lock(); - if (model) { - if (model->isLayeredInFront()) { - return Item::LAYER_3D_FRONT; - } else if (model->isLayeredInHUD()) { - return Item::LAYER_3D_HUD; - } - } - return Item::LAYER_3D; + return _layer; } -ShapeKey ModelMeshPartPayload::getShapeKey() const { - // guard against partially loaded meshes - ModelPointer model = _model.lock(); - if (!model || !model->isLoaded() || !model->getGeometry()) { - return ShapeKey::Builder::invalid(); +void ModelMeshPartPayload::setShapeKey(bool invalidateShapeKey, bool isWireframe) { + if (invalidateShapeKey) { + _shapeKey = ShapeKey::Builder::invalid(); + return; } - const FBXGeometry& geometry = model->getFBXGeometry(); - const auto& networkMeshes = model->getGeometry()->getMeshes(); - - // guard against partially loaded meshes - if (_meshIndex >= (int)networkMeshes.size() || _meshIndex >= (int)geometry.meshes.size() || _meshIndex >= (int)model->_meshStates.size()) { - return ShapeKey::Builder::invalid(); - } - - const FBXMesh& mesh = geometry.meshes.at(_meshIndex); - - // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown - // to false to rebuild out mesh groups. - if (_meshIndex < 0 || _meshIndex >= (int)networkMeshes.size() || _meshIndex > geometry.meshes.size()) { - model->_needsFixupInScene = true; // trigger remove/add cycle - model->invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid - return ShapeKey::Builder::invalid(); - } - - - int vertexCount = mesh.vertices.size(); - if (vertexCount == 0) { - // sanity check - return ShapeKey::Builder::invalid(); // FIXME - } - - model::MaterialKey drawMaterialKey; if (_drawMaterial) { drawMaterialKey = _drawMaterial->getKey(); } bool isTranslucent = drawMaterialKey.isTranslucent(); - bool hasTangents = drawMaterialKey.isNormalMap() && !mesh.tangents.isEmpty(); + bool hasTangents = drawMaterialKey.isNormalMap() && _hasTangents; bool hasSpecular = drawMaterialKey.isMetallicMap(); bool hasLightmap = drawMaterialKey.isLightmapMap(); bool isUnlit = drawMaterialKey.isUnlit(); bool isSkinned = _isSkinned; - bool wireframe = model->isWireframe(); - if (wireframe) { + if (isWireframe) { isTranslucent = hasTangents = hasSpecular = hasLightmap = isSkinned = false; } @@ -500,10 +469,14 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { if (isSkinned) { builder.withSkinned(); } - if (wireframe) { + if (isWireframe) { builder.withWireframe(); } - return builder.build(); + _shapeKey = builder.build(); +} + +ShapeKey ModelMeshPartPayload::getShapeKey() const { + return _shapeKey; } void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) { @@ -515,10 +488,9 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) { batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); batch.setInputFormat((_drawMesh->getVertexFormat())); - ModelPointer model = _model.lock(); - if (model) { - batch.setInputBuffer(0, model->_blendedVertexBuffers[_meshIndex], 0, sizeof(glm::vec3)); - batch.setInputBuffer(1, model->_blendedVertexBuffers[_meshIndex], _drawMesh->getNumVertices() * sizeof(glm::vec3), sizeof(glm::vec3)); + if (_blendedVertexBuffer) { + batch.setInputBuffer(0, _blendedVertexBuffer, 0, sizeof(glm::vec3)); + batch.setInputBuffer(1, _blendedVertexBuffer, _drawMesh->getNumVertices() * sizeof(glm::vec3), sizeof(glm::vec3)); batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2)); } else { batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); @@ -526,11 +498,6 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) { batch.setInputStream(0, _drawMesh->getVertexStream()); } } - - // TODO: Get rid of that extra call - if (!_hasColorAttrib) { - batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - } } void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { @@ -544,31 +511,9 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline: void ModelMeshPartPayload::render(RenderArgs* args) { PerformanceTimer perfTimer("ModelMeshPartPayload::render"); - ModelPointer model = _model.lock(); - if (!model || !model->isAddedToScene() || !model->isVisible()) { - return; // bail asap - } - - if (_state == WAITING_TO_START) { - if (model->isLoaded()) { - _state = STARTED; - model->setRenderItemsNeedUpdate(); - } else { - return; - } - } - - if (_materialNeedsUpdate && model->getGeometry()->areTexturesLoaded()) { - model->setRenderItemsNeedUpdate(); - _materialNeedsUpdate = false; - } - if (!args) { return; } - if (!getShapeKey().isValid()) { - return; - } gpu::Batch& batch = *(args->_batch); auto locations = args->_shapePipeline->locations; diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index 971c6fe90b..fb55883101 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -96,32 +96,32 @@ public: render::ShapeKey getShapeKey() const override; // shape interface void render(RenderArgs* args) override; + void setKey(bool isVisible, bool isLayered); + void setLayer(bool isLayeredInFront, bool isLayeredInHUD); + void setShapeKey(bool invalidateShapeKey, bool isWireframe); + // ModelMeshPartPayload functions to perform render void bindMesh(gpu::Batch& batch) override; void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override; - void initCache(); - void computeAdjustedLocalBound(const std::vector& clusterMatrices); gpu::BufferPointer _clusterBuffer; - ModelWeakPointer _model; int _meshIndex; int _shapeID; bool _isSkinned{ false }; bool _isBlendShaped { false }; - bool _materialNeedsUpdate { true }; + bool _hasTangents { false }; private: + void initCache(const ModelPointer& model); - enum State : uint8_t { - WAITING_TO_START = 0, - STARTED = 1, - }; - - mutable State _state { WAITING_TO_START } ; + gpu::BufferPointer _blendedVertexBuffer; + render::ItemKey _itemKey { render::ItemKey::Builder::opaqueShape().build() }; + render::ShapeKey _shapeKey { render::ShapeKey::Builder::invalid() }; + int _layer { render::Item::LAYER_3D }; }; namespace render { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index d64d841100..fd9f753a75 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -210,6 +210,24 @@ int Model::getRenderInfoTextureCount() { return _renderInfoTextureCount; } +bool Model::shouldInvalidatePayloadShapeKey(int meshIndex) { + if (!getGeometry()) { + return true; + } + + const FBXGeometry& geometry = getFBXGeometry(); + const auto& networkMeshes = getGeometry()->getMeshes(); + // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown + // to false to rebuild out mesh groups. + if (meshIndex < 0 || meshIndex >= (int)networkMeshes.size() || meshIndex >= (int)geometry.meshes.size() || meshIndex >= (int)_meshStates.size()) { + _needsFixupInScene = true; // trigger remove/add cycle + invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid + return true; + } + + return false; +} + void Model::updateRenderItems() { if (!_addedToScene) { return; @@ -237,6 +255,11 @@ void Model::updateRenderItems() { Transform modelTransform = self->getTransform(); modelTransform.setScale(glm::vec3(1.0f)); + bool isWireframe = self->isWireframe(); + bool isVisible = self->isVisible(); + bool isLayeredInFront = self->isLayeredInFront(); + bool isLayeredInHUD = self->isLayeredInHUD(); + render::Transaction transaction; for (int i = 0; i < (int) self->_modelMeshRenderItemIDs.size(); i++) { @@ -244,13 +267,20 @@ void Model::updateRenderItems() { auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex; auto clusterMatrices(self->getMeshState(meshIndex).clusterMatrices); - transaction.updateItem(itemID, [modelTransform, clusterMatrices](ModelMeshPartPayload& data) { + bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex); + + transaction.updateItem(itemID, [modelTransform, clusterMatrices, invalidatePayloadShapeKey, + isWireframe, isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { data.updateClusterBuffer(clusterMatrices); Transform renderTransform = modelTransform; if (clusterMatrices.size() == 1) { renderTransform = modelTransform.worldTransform(Transform(clusterMatrices[0])); } data.updateTransformForSkinnedMesh(renderTransform, modelTransform); + + data.setKey(isVisible, isLayeredInFront || isLayeredInHUD); + data.setLayer(isLayeredInFront, isLayeredInHUD); + data.setShapeKey(invalidatePayloadShapeKey, isWireframe); }); } @@ -272,16 +302,6 @@ void Model::setRenderItemsNeedUpdate() { emit requestRenderUpdate(); } -void Model::initJointTransforms() { - if (isLoaded()) { - glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); - _rig.setModelOffset(modelOffset); - } -} - -void Model::init() { -} - void Model::reset() { if (isLoaded()) { const FBXGeometry& geometry = getFBXGeometry(); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 7568a17342..50ccb22131 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -122,7 +122,6 @@ public: void setIsWireframe(bool isWireframe) { _isWireframe = isWireframe; } bool isWireframe() const { return _isWireframe; } - void init(); void reset(); void setSnapModelToRegistrationPoint(bool snapModelToRegistrationPoint, const glm::vec3& registrationPoint); @@ -346,11 +345,7 @@ protected: // hook for derived classes to be notified when setUrl invalidates the current model. virtual void onInvalidate() {}; - -protected: - virtual void deleteGeometry(); - void initJointTransforms(); QVector _blendshapeCoefficients; @@ -419,6 +414,8 @@ protected: bool _isLayeredInFront { false }; bool _isLayeredInHUD { false }; + bool shouldInvalidatePayloadShapeKey(int meshIndex); + private: float _loadingPriority { 0.0f }; diff --git a/libraries/shared/src/PrioritySortUtil.h b/libraries/shared/src/PrioritySortUtil.h index dc6a877bb9..279fa42ea4 100644 --- a/libraries/shared/src/PrioritySortUtil.h +++ b/libraries/shared/src/PrioritySortUtil.h @@ -28,7 +28,7 @@ glm::vec3 getPosition() const override { return _thing->getPosition(); } float getRadius() const override { return 0.5f * _thing->getBoundingRadius(); } uint64_t getTimestamp() const override { return _thing->getLastTime(); } - const Thing& getThing() const { return _thing; } + Thing getThing() const { return _thing; } private: Thing _thing; }; @@ -43,6 +43,13 @@ (3) Loop over your priority queue and do timeboxed work: + NOTE: Be careful using references to members of instances of T from std::priority_queue. + Under the hood std::priority_queue may re-use instances of T. + For example, after a pop() or a push() the top T may have the same memory address + as the top T before the pop() or push() (but point to a swapped instance of T). + This causes a reference to member variable of T to point to a different value + when operations taken on std::priority_queue shuffle around the instances of T. + uint64_t cutoffTime = usecTimestampNow() + TIME_BUDGET; while (!sortedThings.empty()) { const Thing& thing = sortedThings.top(); diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index 698dd93f29..2b016884f1 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -250,7 +250,8 @@ itemName: name, itemPrice: price ? parseInt(price, 10) : 0, itemHref: href, - referrer: referrer + referrer: referrer, + itemAuthor: author })); } diff --git a/tests/gpu-test/src/TestShapes.cpp b/tests/gpu-test/src/TestShapes.cpp index 253d89cf61..67a348c002 100644 --- a/tests/gpu-test/src/TestShapes.cpp +++ b/tests/gpu-test/src/TestShapes.cpp @@ -29,19 +29,18 @@ void TestShapes::renderTest(size_t testId, RenderArgs* args) { float seconds = secTimestampNow() - startSecs; seconds /= 4.0f; batch.setModelTransform(Transform()); - batch._glColor4f(0.8f, 0.25f, 0.25f, 1.0f); + const auto color = glm::vec4(0.8f, 0.25f, 0.25f, 1.0f); bool wire = (seconds - floorf(seconds) > 0.5f); int shapeIndex = ((int)seconds) % TYPE_COUNT; if (wire) { - geometryCache->renderWireShape(batch, SHAPE[shapeIndex]); + geometryCache->renderWireShape(batch, SHAPE[shapeIndex], color); } else { - geometryCache->renderShape(batch, SHAPE[shapeIndex]); + geometryCache->renderShape(batch, SHAPE[shapeIndex], color); } batch.setModelTransform(Transform().setScale(1.01f)); - batch._glColor4f(1, 1, 1, 1); - geometryCache->renderWireCube(batch); + geometryCache->renderWireCube(batch, glm::vec4(1,1,1,1)); }