mirror of
https://github.com/overte-org/overte.git
synced 2025-08-04 08:43:47 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into polyobjimportfixes
This commit is contained in:
commit
01fcd453b8
39 changed files with 293 additions and 199 deletions
27
BUILD_LINUX_CHEATSHEET.md
Normal file
27
BUILD_LINUX_CHEATSHEET.md
Normal file
|
@ -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
|
|
@ -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--;
|
||||
|
||||
|
|
|
@ -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 <b>will not</b> be added to your <b>Purchases</b>. 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 = '<i>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.</i>';
|
||||
buyTextContainer.color = hifi.colors.white;
|
||||
buyTextContainer.border.color = hifi.colors.white;
|
||||
buyGlyph.text = "";
|
||||
buyGlyph.size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>(_avatar)->getBoundingRadius(); }
|
||||
uint64_t getTimestamp() const override { return std::static_pointer_cast<Avatar>(_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<Avatar>(sortData.getAvatar());
|
||||
const auto avatar = std::static_pointer_cast<Avatar>(sortData.getAvatar());
|
||||
|
||||
bool ignoring = DependencyManager::get<NodeList>()->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<Avatar>(newSortData.getAvatar());
|
||||
const auto newAvatar = std::static_pointer_cast<Avatar>(newSortData.getAvatar());
|
||||
inView = newSortData.getPriority() > OUT_OF_VIEW_THRESHOLD;
|
||||
if (inView && newAvatar->hasNewJointData()) {
|
||||
numAVatarsNotUpdated++;
|
||||
|
|
|
@ -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<Wallet>();
|
||||
QString signature = key.isEmpty() ? "" : wallet->signWithKey(text, key);
|
||||
QString signature = wallet->signWithKey(text, key);
|
||||
QJsonObject request;
|
||||
request[propertyName] = QString(text);
|
||||
if (!controlled_failure) {
|
||||
|
|
|
@ -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<ReceivedMessage> 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();
|
||||
|
|
|
@ -24,7 +24,6 @@ ModelOverlay::ModelOverlay()
|
|||
: _model(std::make_shared<Model>(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;
|
||||
|
|
|
@ -138,7 +138,6 @@ Avatar::~Avatar() {
|
|||
|
||||
void Avatar::init() {
|
||||
getHead()->init();
|
||||
_skeletonModel->init();
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<EntityItemID, EntityRendererPointer>::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();
|
||||
|
|
|
@ -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; });
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<GeometryCache>()->bindWebBrowserProgram(batch, fadeRatio < OPAQUE_ALPHA_THRESHOLD);
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, glm::vec4(1.0f, 1.0f, 1.0f, fadeRatio), _geometryId);
|
||||
|
|
|
@ -2530,7 +2530,8 @@ bool EntityItemProperties::verifySignature(const QString& publicKey, const QByte
|
|||
return false;
|
||||
}
|
||||
|
||||
const unsigned char* key = reinterpret_cast<const unsigned char*>(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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -253,6 +253,7 @@ protected:
|
|||
|
||||
struct InputStageState {
|
||||
bool _invalidFormat { true };
|
||||
bool _hadColorAttribute{ true };
|
||||
Stream::FormatPointer _format;
|
||||
std::string _formatKey;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -191,6 +191,8 @@ void Connection::queueReceivedMessagePacket(std::unique_ptr<Packet> 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> 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() {
|
||||
|
|
|
@ -30,7 +30,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
case PacketType::EntityEdit:
|
||||
case PacketType::EntityData:
|
||||
case PacketType::EntityPhysics:
|
||||
return static_cast<PacketVersion>(EntityVersion::StaticCertJsonVersionOne);
|
||||
return static_cast<PacketVersion>(EntityVersion::OwnershipChallengeFix);
|
||||
|
||||
case PacketType::EntityQuery:
|
||||
return static_cast<PacketVersion>(EntityQueryPacketVersion::ConnectionIdentifier);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<NLPacket>& bufferedPacket = _pendingEditPackets[nodeUUID].first; //only a NLPacket for now
|
||||
|
|
|
@ -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<CauterizedModel*>(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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<CauterizedMeshPartPayload>(itemID, [modelTransform, clusterMatrices, clusterMatricesCauterized](CauterizedMeshPartPayload& data) {
|
||||
bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex);
|
||||
|
||||
transaction.updateItem<CauterizedMeshPartPayload>(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);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<glm::mat4>& 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 {
|
||||
|
|
|
@ -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<ModelMeshPartPayload>(itemID, [modelTransform, clusterMatrices](ModelMeshPartPayload& data) {
|
||||
bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex);
|
||||
|
||||
transaction.updateItem<ModelMeshPartPayload>(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();
|
||||
|
|
|
@ -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<float> _blendshapeCoefficients;
|
||||
|
||||
|
@ -419,6 +414,8 @@ protected:
|
|||
bool _isLayeredInFront { false };
|
||||
bool _isLayeredInHUD { false };
|
||||
|
||||
bool shouldInvalidatePayloadShapeKey(int meshIndex);
|
||||
|
||||
private:
|
||||
float _loadingPriority { 0.0f };
|
||||
|
||||
|
|
|
@ -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<T>.
|
||||
Under the hood std::priority_queue<T> 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<T> shuffle around the instances of T.
|
||||
|
||||
uint64_t cutoffTime = usecTimestampNow() + TIME_BUDGET;
|
||||
while (!sortedThings.empty()) {
|
||||
const Thing& thing = sortedThings.top();
|
||||
|
|
|
@ -250,7 +250,8 @@
|
|||
itemName: name,
|
||||
itemPrice: price ? parseInt(price, 10) : 0,
|
||||
itemHref: href,
|
||||
referrer: referrer
|
||||
referrer: referrer,
|
||||
itemAuthor: author
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue