Merge branch 'master' of github.com:highfidelity/hifi into scale-wearables-with-avatar-3

This commit is contained in:
Seth Alves 2017-12-22 15:13:02 -08:00
commit 96197e66bb
33 changed files with 243 additions and 189 deletions

View file

@ -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;
}
}

View file

@ -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) {

View file

@ -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();

View file

@ -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;

View file

@ -138,7 +138,6 @@ Avatar::~Avatar() {
void Avatar::init() {
getHead()->init();
_skeletonModel->init();
_initialized = true;
}

View file

@ -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; });
}

View file

@ -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 {

View file

@ -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

View file

@ -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);

View file

@ -2551,7 +2551,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);
@ -2569,19 +2570,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) {
@ -2590,7 +2595,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);

View file

@ -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;

View file

@ -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();
}

View file

@ -253,6 +253,7 @@ protected:
struct InputStageState {
bool _invalidFormat { true };
bool _hadColorAttribute{ true };
Stream::FormatPointer _format;
std::string _formatKey;

View file

@ -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;

View file

@ -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

View file

@ -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));

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -200,7 +200,8 @@ enum class EntityVersion : PacketVersion {
StrokeColorProperty = 77,
HasDynamicOwnershipTests,
HazeEffect,
StaticCertJsonVersionOne
StaticCertJsonVersionOne,
OwnershipChallengeFix,
};
enum class EntityScriptCallMethodVersion : PacketVersion {

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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);
});
}

View file

@ -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,

View file

@ -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,

View file

@ -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;

View file

@ -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 {

View file

@ -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();

View file

@ -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 };

View file

@ -250,7 +250,8 @@
itemName: name,
itemPrice: price ? parseInt(price, 10) : 0,
itemHref: href,
referrer: referrer
referrer: referrer,
itemAuthor: author
}));
}

View file

@ -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));
}