Merge branch 'master' into 20639

This commit is contained in:
David Rowe 2015-08-07 12:33:13 -07:00
commit b70c1a4960
37 changed files with 271 additions and 162 deletions

View file

@ -5,6 +5,7 @@
* [OpenSSL](https://www.openssl.org/related/binaries.html) ~> 1.0.1m * [OpenSSL](https://www.openssl.org/related/binaries.html) ~> 1.0.1m
* IMPORTANT: Using the recommended version of OpenSSL is critical to avoid security vulnerabilities. * IMPORTANT: Using the recommended version of OpenSSL is critical to avoid security vulnerabilities.
* [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional) * [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional)
* [zlib](http://www.zlib.net/)
####CMake External Project Dependencies ####CMake External Project Dependencies

View file

@ -1,7 +1,7 @@
Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only OS X specific instructions are found in this file. Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only OS X specific instructions are found in this file.
###Homebrew ###Homebrew
[Homebrew](http://brew.sh/) is an excellent package manager for OS X. It makes install of all hifi dependencies very simple. [Homebrew](http://brew.sh/) is an excellent package manager for OS X. It makes install of all High Fidelity dependencies very simple.
brew tap highfidelity/homebrew-formulas brew tap highfidelity/homebrew-formulas
brew install cmake openssl brew install cmake openssl

View file

@ -75,6 +75,16 @@ To prevent these problems, install OpenSSL yourself. Download the following bina
Install OpenSSL into the Windows system directory, to make sure that Qt uses the version that you've just installed, and not some other version. Install OpenSSL into the Windows system directory, to make sure that Qt uses the version that you've just installed, and not some other version.
####zlib
Install zlib from
[Zlib for Windows](http://gnuwin32.sourceforge.net/packages/zlib.htm)
and fix a header file, as described here:
[zlib zconf.h bug](http://sourceforge.net/p/gnuwin32/bugs/169/)
###Build High Fidelity using Visual Studio ###Build High Fidelity using Visual Studio
Follow the same build steps from the CMake section of [BUILD.md](BUILD.md), but pass a different generator to CMake. Follow the same build steps from the CMake section of [BUILD.md](BUILD.md), but pass a different generator to CMake.

View file

@ -580,7 +580,6 @@ void DomainServer::processConnectRequestPacket(QSharedPointer<NLPacket> packet)
NodeType_t nodeType; NodeType_t nodeType;
HifiSockAddr publicSockAddr, localSockAddr; HifiSockAddr publicSockAddr, localSockAddr;
if (packet->getPayloadSize() == 0) { if (packet->getPayloadSize() == 0) {
return; return;
} }
@ -625,31 +624,62 @@ void DomainServer::processConnectRequestPacket(QSharedPointer<NLPacket> packet)
return; return;
} }
} }
} }
QList<NodeType_t> nodeInterestList; QList<NodeType_t> nodeInterestList;
QString username; QString username;
QByteArray usernameSignature; QByteArray usernameSignature;
packetStream >> nodeInterestList >> username >> usernameSignature;
auto limitedNodeList = DependencyManager::get<LimitedNodeList>(); auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
packetStream >> nodeInterestList;
if (packet->bytesLeftToRead() > 0) {
// try to verify username
packetStream >> username;
}
bool isRestrictingAccess =
_settingsManager.valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool();
// we always let in a user who is sending a packet from our local socket or from the localhost address
bool isLocalUser = (senderSockAddr.getAddress() == DependencyManager::get<LimitedNodeList>()->getLocalSockAddr().getAddress() || senderSockAddr.getAddress() == QHostAddress::LocalHost);
if (isRestrictingAccess && !isLocalUser) {
if (!username.isEmpty()) {
// if there's a username, try to unpack username signature
packetStream >> usernameSignature;
if (usernameSignature.isEmpty()) {
// if user didn't include usernameSignature in connect request, send a connectionToken packet
QUuid& connectionToken = _connectionTokenHash[username.toLower()];
if (connectionToken.isNull()) {
connectionToken = QUuid::createUuid();
}
static auto connectionTokenPacket = NLPacket::create(PacketType::DomainServerConnectionToken, NUM_BYTES_RFC4122_UUID);
connectionTokenPacket->reset();
connectionTokenPacket->write(connectionToken.toRfc4122());
limitedNodeList->sendUnreliablePacket(*connectionTokenPacket, packet->getSenderSockAddr());
return;
}
}
}
QString reason; QString reason;
if (!isAssignment && !shouldAllowConnectionFromNode(username, usernameSignature, senderSockAddr, reason)) { if (!isAssignment && !shouldAllowConnectionFromNode(username, usernameSignature, senderSockAddr, reason)) {
// this is an agent and we've decided we won't let them connect - send them a packet to deny connection // this is an agent and we've decided we won't let them connect - send them a packet to deny connection
QByteArray utfString = reason.toUtf8(); QByteArray utfString = reason.toUtf8();
quint16 payloadSize = utfString.size(); quint16 payloadSize = utfString.size();
auto connectionDeniedPacket = NLPacket::create(PacketType::DomainConnectionDenied, payloadSize + sizeof(payloadSize)); auto connectionDeniedPacket = NLPacket::create(PacketType::DomainConnectionDenied, payloadSize + sizeof(payloadSize));
if (payloadSize > 0) {
connectionDeniedPacket->writePrimitive(payloadSize); connectionDeniedPacket->writePrimitive(payloadSize);
connectionDeniedPacket->write(utfString); connectionDeniedPacket->write(utfString);
}
// tell client it has been refused. // tell client it has been refused.
limitedNodeList->sendPacket(std::move(connectionDeniedPacket), senderSockAddr); limitedNodeList->sendPacket(std::move(connectionDeniedPacket), senderSockAddr);
return; return;
} }
@ -736,6 +766,7 @@ void DomainServer::processConnectRequestPacket(QSharedPointer<NLPacket> packet)
} }
} }
void DomainServer::processListRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) { void DomainServer::processListRequestPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode) {
NodeType_t throwawayNodeType; NodeType_t throwawayNodeType;
@ -766,13 +797,16 @@ unsigned int DomainServer::countConnectedUsers() {
} }
bool DomainServer::verifyUsersKey(const QString& username, bool DomainServer::verifyUserSignature(const QString& username,
const QByteArray& usernameSignature, const QByteArray& usernameSignature,
QString& reasonReturn) { QString& reasonReturn) {
// it's possible this user can be allowed to connect, but we need to check their username signature
// it's possible this user can be allowed to connect, but we need to check their username signature
QByteArray publicKeyArray = _userPublicKeys.value(username); QByteArray publicKeyArray = _userPublicKeys.value(username);
if (!publicKeyArray.isEmpty()) {
const QUuid& connectionToken = _connectionTokenHash.value(username.toLower());
if (!publicKeyArray.isEmpty() && !connectionToken.isNull()) {
// if we do have a public key for the user, check for a signature match // if we do have a public key for the user, check for a signature match
const unsigned char* publicKeyData = reinterpret_cast<const unsigned char*>(publicKeyArray.constData()); const unsigned char* publicKeyData = reinterpret_cast<const unsigned char*>(publicKeyArray.constData());
@ -780,38 +814,44 @@ bool DomainServer::verifyUsersKey(const QString& username,
// first load up the public key into an RSA struct // first load up the public key into an RSA struct
RSA* rsaPublicKey = d2i_RSA_PUBKEY(NULL, &publicKeyData, publicKeyArray.size()); RSA* rsaPublicKey = d2i_RSA_PUBKEY(NULL, &publicKeyData, publicKeyArray.size());
QByteArray lowercaseUsername = username.toLower().toUtf8();
QByteArray usernameWithToken = QCryptographicHash::hash(lowercaseUsername.append(connectionToken.toRfc4122()),
QCryptographicHash::Sha256);
if (rsaPublicKey) { if (rsaPublicKey) {
QByteArray decryptedArray(RSA_size(rsaPublicKey), 0); QByteArray decryptedArray(RSA_size(rsaPublicKey), 0);
int decryptResult = int decryptResult = RSA_verify(NID_sha256,
RSA_public_decrypt(usernameSignature.size(), reinterpret_cast<const unsigned char*>(usernameWithToken.constData()),
usernameWithToken.size(),
reinterpret_cast<const unsigned char*>(usernameSignature.constData()), reinterpret_cast<const unsigned char*>(usernameSignature.constData()),
reinterpret_cast<unsigned char*>(decryptedArray.data()), usernameSignature.size(),
rsaPublicKey, RSA_PKCS1_PADDING); rsaPublicKey);
if (decryptResult != -1) { if (decryptResult == 1) {
if (username.toLower() == decryptedArray) {
qDebug() << "Username signature matches for" << username << "- allowing connection."; qDebug() << "Username signature matches for" << username << "- allowing connection.";
// free up the public key before we return // free up the public key and remove connection token before we return
RSA_free(rsaPublicKey); RSA_free(rsaPublicKey);
_connectionTokenHash.remove(username);
return true; return true;
} else {
qDebug() << "Username signature did not match for" << username << "- denying connection.";
reasonReturn = "Username signature did not match.";
}
} else {
qDebug() << "Couldn't decrypt user signature for" << username << "- denying connection.";
reasonReturn = "Couldn't decrypt user signature.";
}
} else {
qDebug() << "Error decrypting username signature for " << username << "- denying connection.";
reasonReturn = "Error decrypting username signature.";
// free up the public key, we don't need it anymore // free up the public key, we don't need it anymore
RSA_free(rsaPublicKey); RSA_free(rsaPublicKey);
}
} else { } else {
// we can't let this user in since we couldn't convert their public key to an RSA key we could use // we can't let this user in since we couldn't convert their public key to an RSA key we could use
qDebug() << "Couldn't convert data to RSA key for" << username << "- denying connection."; qDebug() << "Couldn't convert data to RSA key for" << username << "- denying connection.";
reasonReturn = "Couldn't convert data to RSA key."; reasonReturn = "Couldn't convert data to RSA key.";
} }
} else {
qDebug() << "Insufficient data to decrypt username signature - denying connection.";
reasonReturn = "Insufficient data";
} }
requestUserPublicKey(username); // no joy. maybe next time? requestUserPublicKey(username); // no joy. maybe next time?
@ -824,22 +864,22 @@ bool DomainServer::shouldAllowConnectionFromNode(const QString& username,
const HifiSockAddr& senderSockAddr, const HifiSockAddr& senderSockAddr,
QString& reasonReturn) { QString& reasonReturn) {
//TODO: improve flow so these bools aren't declared twice
bool isRestrictingAccess = bool isRestrictingAccess =
_settingsManager.valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool(); _settingsManager.valueOrDefaultValueForKeyPath(RESTRICTED_ACCESS_SETTINGS_KEYPATH).toBool();
bool isLocalUser = (senderSockAddr.getAddress() == DependencyManager::get<LimitedNodeList>()->getLocalSockAddr().getAddress() || senderSockAddr.getAddress() == QHostAddress::LocalHost);
// we always let in a user who is sending a packet from our local socket or from the localhost address if (isRestrictingAccess && !isLocalUser) {
if (senderSockAddr.getAddress() == DependencyManager::get<LimitedNodeList>()->getLocalSockAddr().getAddress()
|| senderSockAddr.getAddress() == QHostAddress::LocalHost) {
return true;
}
if (isRestrictingAccess) {
QStringList allowedUsers = QStringList allowedUsers =
_settingsManager.valueOrDefaultValueForKeyPath(ALLOWED_USERS_SETTINGS_KEYPATH).toStringList(); _settingsManager.valueOrDefaultValueForKeyPath(ALLOWED_USERS_SETTINGS_KEYPATH).toStringList();
if (allowedUsers.contains(username, Qt::CaseInsensitive)) { if (allowedUsers.contains(username, Qt::CaseInsensitive)) {
if (!verifyUsersKey(username, usernameSignature, reasonReturn)) { if (username.isEmpty()) {
qDebug() << "Connect request denied - no username provided.";
reasonReturn = "No username provided";
return false;
}
if (!verifyUserSignature(username, usernameSignature, reasonReturn)) {
return false; return false;
} }
} else { } else {
@ -851,13 +891,12 @@ bool DomainServer::shouldAllowConnectionFromNode(const QString& username,
} }
// either we aren't restricting users, or this user is in the allowed list // either we aren't restricting users, or this user is in the allowed list
// if this user is in the editors list, exempt them from the max-capacity check // if this user is in the editors list, exempt them from the max-capacity check
const QVariant* allowedEditorsVariant = const QVariant* allowedEditorsVariant =
valueForKeyPath(_settingsManager.getSettingsMap(), ALLOWED_EDITORS_SETTINGS_KEYPATH); valueForKeyPath(_settingsManager.getSettingsMap(), ALLOWED_EDITORS_SETTINGS_KEYPATH);
QStringList allowedEditors = allowedEditorsVariant ? allowedEditorsVariant->toStringList() : QStringList(); QStringList allowedEditors = allowedEditorsVariant ? allowedEditorsVariant->toStringList() : QStringList();
if (allowedEditors.contains(username)) { if (allowedEditors.contains(username)) {
if (verifyUsersKey(username, usernameSignature, reasonReturn)) { if (verifyUserSignature(username, usernameSignature, reasonReturn)) {
return true; return true;
} }
} }

View file

@ -90,7 +90,7 @@ private:
void pingPunchForConnectingPeer(const SharedNetworkPeer& peer); void pingPunchForConnectingPeer(const SharedNetworkPeer& peer);
unsigned int countConnectedUsers(); unsigned int countConnectedUsers();
bool verifyUsersKey (const QString& username, const QByteArray& usernameSignature, QString& reasonReturn); bool verifyUserSignature (const QString& username, const QByteArray& usernameSignature, QString& reasonReturn);
bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature, bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature,
const HifiSockAddr& senderSockAddr, QString& reasonReturn); const HifiSockAddr& senderSockAddr, QString& reasonReturn);
@ -150,6 +150,8 @@ private:
QSet<QUuid> _webAuthenticationStateSet; QSet<QUuid> _webAuthenticationStateSet;
QHash<QUuid, DomainServerWebSessionData> _cookieSessionHash; QHash<QUuid, DomainServerWebSessionData> _cookieSessionHash;
QHash<QString, QUuid> _connectionTokenHash;
QHash<QString, QByteArray> _userPublicKeys; QHash<QString, QByteArray> _userPublicKeys;
QHash<QUuid, SharedNetworkPeer> _icePeers; QHash<QUuid, SharedNetworkPeer> _icePeers;

Binary file not shown.

View file

@ -107,7 +107,7 @@ JointReferential::JointReferential(Referential* referential, EntityTree* tree, A
EntityItemPointer item = _tree->findEntityByID(_entityID); EntityItemPointer item = _tree->findEntityByID(_entityID);
const Model* model = getModel(item); const Model* model = getModel(item);
if (!isValid() || model == NULL || _jointIndex >= (uint32_t)(model->getJointStateCount())) { if (isValid() && model != NULL && _jointIndex < (uint32_t)(model->getJointStateCount())) {
_lastRefDimension = item->getDimensions(); _lastRefDimension = item->getDimensions();
model->getJointRotationInWorldFrame(_jointIndex, _refRotation); model->getJointRotationInWorldFrame(_jointIndex, _refRotation);
model->getJointPositionInWorldFrame(_jointIndex, _refPosition); model->getJointPositionInWorldFrame(_jointIndex, _refPosition);

View file

@ -47,9 +47,6 @@
#include "Util.h" #include "Util.h"
#include "InterfaceLogging.h" #include "InterfaceLogging.h"
#include "gpu/GLBackend.h"
using namespace std; using namespace std;
const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f); const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f);

View file

@ -97,7 +97,6 @@ void SkeletonModel::initJointStates(QVector<JointState> states) {
} }
const float PALM_PRIORITY = DEFAULT_PRIORITY; const float PALM_PRIORITY = DEFAULT_PRIORITY;
const float LEAN_PRIORITY = DEFAULT_PRIORITY;
void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
if (_owningAvatar->isMyAvatar()) { if (_owningAvatar->isMyAvatar()) {

View file

@ -14,7 +14,6 @@
#include <avatar/AvatarManager.h> #include <avatar/AvatarManager.h>
#include <DeferredLightingEffect.h> #include <DeferredLightingEffect.h>
#include <GLMHelpers.h> #include <GLMHelpers.h>
#include <gpu/GLBackend.h>
#include <gpu/GLBackendShared.h> #include <gpu/GLBackendShared.h>
#include <FramebufferCache.h> #include <FramebufferCache.h>
#include <GLMHelpers.h> #include <GLMHelpers.h>
@ -32,7 +31,6 @@
#include "ui/AvatarInputs.h" #include "ui/AvatarInputs.h"
const vec4 CONNECTION_STATUS_BORDER_COLOR{ 1.0f, 0.0f, 0.0f, 0.8f }; const vec4 CONNECTION_STATUS_BORDER_COLOR{ 1.0f, 0.0f, 0.0f, 0.8f };
const float CONNECTION_STATUS_BORDER_LINE_WIDTH = 4.0f;
static const float ORTHO_NEAR_CLIP = -1000.0f; static const float ORTHO_NEAR_CLIP = -1000.0f;
static const float ORTHO_FAR_CLIP = 1000.0f; static const float ORTHO_FAR_CLIP = 1000.0f;

View file

@ -105,7 +105,7 @@ void BillboardOverlay::render(RenderArgs* args) {
batch->setModelTransform(transform); batch->setModelTransform(transform);
batch->setResourceTexture(0, _texture->getGPUTexture()); batch->setResourceTexture(0, _texture->getGPUTexture());
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(*batch, true, true, false, true); DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(*batch, true, false, false, true);
DependencyManager::get<GeometryCache>()->renderQuad( DependencyManager::get<GeometryCache>()->renderQuad(
*batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, *batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha) glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha)

View file

@ -15,7 +15,6 @@
#include <limits> #include <limits>
#include <render/Scene.h> #include <render/Scene.h>
#include <gpu/GLBackend.h>
#include <RegisteredMetaTypes.h> #include <RegisteredMetaTypes.h>
#include "Application.h" #include "Application.h"

View file

@ -15,7 +15,6 @@
#include <DependencyManager.h> #include <DependencyManager.h>
#include <GeometryCache.h> #include <GeometryCache.h>
#include <GLMHelpers.h> #include <GLMHelpers.h>
#include <gpu/GLBackend.h>
#include <OffscreenUi.h> #include <OffscreenUi.h>
#include <RegisteredMetaTypes.h> #include <RegisteredMetaTypes.h>
#include <SharedUtil.h> #include <SharedUtil.h>

View file

@ -70,6 +70,7 @@ void Rig::startAnimation(const QString& url, float fps, float priority,
handle = createAnimationHandle(); handle = createAnimationHandle();
handle->setURL(url); handle->setURL(url);
} }
handle->setFade(1.0f); // If you want to fade, use the startAnimationByRole system.
handle->setFPS(fps); handle->setFPS(fps);
handle->setPriority(priority); handle->setPriority(priority);
handle->setLoop(loop); handle->setLoop(loop);
@ -150,7 +151,8 @@ void Rig::stopAnimationByRole(const QString& role) {
void Rig::stopAnimation(const QString& url) { void Rig::stopAnimation(const QString& url) {
foreach (const AnimationHandlePointer& handle, getRunningAnimations()) { foreach (const AnimationHandlePointer& handle, getRunningAnimations()) {
if (handle->getURL() == url) { if (handle->getURL() == url) {
handle->stop(); handle->setFade(0.0f); // right away. Will be remove during updateAnimations, without locking
handle->setFadePerSecond(-1.0f); // so that the updateAnimation code notices
} }
} }
} }
@ -472,7 +474,9 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) {
float fadeSumSoFar = 0.0f; float fadeSumSoFar = 0.0f;
foreach (const AnimationHandlePointer& handle, _runningAnimations) { foreach (const AnimationHandlePointer& handle, _runningAnimations) {
handle->setPriority(1.0f); handle->setPriority(1.0f);
float normalizedFade = handle->getFade() / fadeTotal; // if no fadeTotal, everyone's (typically just one running) is starting at zero. In that case, blend equally.
float normalizedFade = (fadeTotal != 0.0f) ? (handle->getFade() / fadeTotal) : (1.0f / _runningAnimations.count());
assert(normalizedFade != 0.0f);
// simulate() will blend each animation result into the result so far, based on the pairwise mix at at each step. // simulate() will blend each animation result into the result so far, based on the pairwise mix at at each step.
// i.e., slerp the 'mix' distance from the result so far towards this iteration's animation result. // i.e., slerp the 'mix' distance from the result so far towards this iteration's animation result.
// The formula here for mix is based on the idea that, at each step: // The formula here for mix is based on the idea that, at each step:

View file

@ -976,6 +976,8 @@ ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex) {
data.extracted.mesh.meshIndex = meshIndex++; data.extracted.mesh.meshIndex = meshIndex++;
QVector<int> materials; QVector<int> materials;
QVector<int> textures; QVector<int> textures;
bool isMaterialPerPolygon = false;
foreach (const FBXNode& child, object.children) { foreach (const FBXNode& child, object.children) {
if (child.name == "Vertices") { if (child.name == "Vertices") {
data.vertices = createVec3Vector(getDoubleVector(child)); data.vertices = createVec3Vector(getDoubleVector(child));
@ -1105,8 +1107,16 @@ ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex) {
foreach (const FBXNode& subdata, child.children) { foreach (const FBXNode& subdata, child.children) {
if (subdata.name == "Materials") { if (subdata.name == "Materials") {
materials = getIntVector(subdata); materials = getIntVector(subdata);
} else if (subdata.name == "MappingInformationType") {
if (subdata.properties.at(0) == "ByPolygon") {
isMaterialPerPolygon = true;
} else {
isMaterialPerPolygon = false;
} }
} }
}
} else if (child.name == "LayerElementTexture") { } else if (child.name == "LayerElementTexture") {
foreach (const FBXNode& subdata, child.children) { foreach (const FBXNode& subdata, child.children) {
if (subdata.name == "TextureId") { if (subdata.name == "TextureId") {
@ -1116,6 +1126,12 @@ ExtractedMesh extractMesh(const FBXNode& object, unsigned int& meshIndex) {
} }
} }
bool isMultiMaterial = false;
if (isMaterialPerPolygon) {
isMultiMaterial = true;
}
// convert the polygons to quads and triangles // convert the polygons to quads and triangles
int polygonIndex = 0; int polygonIndex = 0;
QHash<QPair<int, int>, int> materialTextureParts; QHash<QPair<int, int>, int> materialTextureParts;
@ -2104,6 +2120,9 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping,
if (type.contains("diffuse")) { if (type.contains("diffuse")) {
diffuseTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); diffuseTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else if (type.contains("transparentcolor")) { // it should be TransparentColor...
// THis is how Maya assign a texture that affect diffuse color AND transparency ?
diffuseTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else if (type.contains("bump") || type.contains("normal")) { } else if (type.contains("bump") || type.contains("normal")) {
bumpTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); bumpTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));

View file

@ -153,7 +153,6 @@ void GLBackend::releaseUniformBuffer(uint32_t slot) {
if (buf) { if (buf) {
auto* object = Backend::getGPUObject<GLBackend::GLBuffer>(*buf); auto* object = Backend::getGPUObject<GLBackend::GLBuffer>(*buf);
if (object) { if (object) {
GLuint bo = object->_buffer;
glBindBufferBase(GL_UNIFORM_BUFFER, slot, 0); // RELEASE glBindBufferBase(GL_UNIFORM_BUFFER, slot, 0); // RELEASE
(void) CHECK_GL_ERROR(); (void) CHECK_GL_ERROR();
@ -222,7 +221,6 @@ void GLBackend::releaseResourceTexture(uint32_t slot) {
if (tex) { if (tex) {
auto* object = Backend::getGPUObject<GLBackend::GLTexture>(*tex); auto* object = Backend::getGPUObject<GLBackend::GLTexture>(*tex);
if (object) { if (object) {
GLuint to = object->_texture;
GLuint target = object->_target; GLuint target = object->_target;
glActiveTexture(GL_TEXTURE0 + slot); glActiveTexture(GL_TEXTURE0 + slot);
glBindTexture(target, 0); // RELEASE glBindTexture(target, 0); // RELEASE

View file

@ -10,11 +10,13 @@
// //
#include <openssl/rsa.h> #include <openssl/rsa.h>
#include <openssl/x509.h>
#include <qjsondocument.h> #include <qjsondocument.h>
#include <QtCore/QDebug> #include <QtCore/QDebug>
#include <QtCore/QDataStream> #include <QtCore/QDataStream>
#include "UUID.h"
#include "NetworkLogging.h" #include "NetworkLogging.h"
#include "DataServerAccountInfo.h" #include "DataServerAccountInfo.h"
@ -30,8 +32,7 @@ DataServerAccountInfo::DataServerAccountInfo() :
_walletID(), _walletID(),
_balance(0), _balance(0),
_hasBalance(false), _hasBalance(false),
_privateKey(), _privateKey()
_usernameSignature()
{ {
} }
@ -74,9 +75,6 @@ void DataServerAccountInfo::setUsername(const QString& username) {
if (_username != username) { if (_username != username) {
_username = username; _username = username;
// clear our username signature so it has to be re-created
_usernameSignature = QByteArray();
qCDebug(networking) << "Username changed to" << username; qCDebug(networking) << "Username changed to" << username;
} }
} }
@ -128,8 +126,8 @@ void DataServerAccountInfo::setProfileInfoFromJSON(const QJsonObject& jsonObject
setWalletID(QUuid(user["wallet_id"].toString())); setWalletID(QUuid(user["wallet_id"].toString()));
} }
const QByteArray& DataServerAccountInfo::getUsernameSignature() { QByteArray DataServerAccountInfo::getUsernameSignature(const QUuid& connectionToken) {
if (_usernameSignature.isEmpty()) {
if (!_privateKey.isEmpty()) { if (!_privateKey.isEmpty()) {
const char* privateKeyData = _privateKey.constData(); const char* privateKeyData = _privateKey.constData();
RSA* rsaPrivateKey = d2i_RSAPrivateKey(NULL, RSA* rsaPrivateKey = d2i_RSAPrivateKey(NULL,
@ -137,36 +135,41 @@ const QByteArray& DataServerAccountInfo::getUsernameSignature() {
_privateKey.size()); _privateKey.size());
if (rsaPrivateKey) { if (rsaPrivateKey) {
QByteArray lowercaseUsername = _username.toLower().toUtf8(); QByteArray lowercaseUsername = _username.toLower().toUtf8();
_usernameSignature.resize(RSA_size(rsaPrivateKey)); QByteArray usernameWithToken = QCryptographicHash::hash(lowercaseUsername.append(connectionToken.toRfc4122()),
QCryptographicHash::Sha256);
int encryptReturn = RSA_private_encrypt(lowercaseUsername.size(), QByteArray usernameSignature(RSA_size(rsaPrivateKey), 0);
reinterpret_cast<const unsigned char*>(lowercaseUsername.constData()), unsigned int usernameSignatureSize = 0;
reinterpret_cast<unsigned char*>(_usernameSignature.data()),
rsaPrivateKey, RSA_PKCS1_PADDING); int encryptReturn = RSA_sign(NID_sha256,
reinterpret_cast<const unsigned char*>(usernameWithToken.constData()),
usernameWithToken.size(),
reinterpret_cast<unsigned char*>(usernameSignature.data()),
&usernameSignatureSize,
rsaPrivateKey);
// free the private key RSA struct now that we are done with it
RSA_free(rsaPrivateKey);
if (encryptReturn == -1) { if (encryptReturn == -1) {
qCDebug(networking) << "Error encrypting username signature."; qCDebug(networking) << "Error encrypting username signature.";
qCDebug(networking) << "Will re-attempt on next domain-server check in."; qCDebug(networking) << "Will re-attempt on next domain-server check in.";
_usernameSignature = QByteArray(); } else {
qDebug(networking) << "Returning username" << _username << "signed with connection UUID" << uuidStringWithoutCurlyBraces(connectionToken);
return usernameSignature;
} }
// free the private key RSA struct now that we are done with it
RSA_free(rsaPrivateKey);
} else { } else {
qCDebug(networking) << "Could not create RSA struct from QByteArray private key."; qCDebug(networking) << "Could not create RSA struct from QByteArray private key.";
qCDebug(networking) << "Will re-attempt on next domain-server check in."; qCDebug(networking) << "Will re-attempt on next domain-server check in.";
} }
} }
} return QByteArray();
return _usernameSignature;
} }
void DataServerAccountInfo::setPrivateKey(const QByteArray& privateKey) { void DataServerAccountInfo::setPrivateKey(const QByteArray& privateKey) {
_privateKey = privateKey; _privateKey = privateKey;
// clear our username signature so it has to be re-created
_usernameSignature = QByteArray();
} }
QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) { QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) {

View file

@ -43,7 +43,7 @@ public:
const QUuid& getWalletID() const { return _walletID; } const QUuid& getWalletID() const { return _walletID; }
void setWalletID(const QUuid& walletID); void setWalletID(const QUuid& walletID);
const QByteArray& getUsernameSignature(); QByteArray getUsernameSignature(const QUuid& connectionToken);
bool hasPrivateKey() const { return !_privateKey.isEmpty(); } bool hasPrivateKey() const { return !_privateKey.isEmpty(); }
void setPrivateKey(const QByteArray& privateKey); void setPrivateKey(const QByteArray& privateKey);
@ -73,7 +73,7 @@ private:
qint64 _balance; qint64 _balance;
bool _hasBalance; bool _hasBalance;
QByteArray _privateKey; QByteArray _privateKey;
QByteArray _usernameSignature;
}; };
#endif // hifi_DataServerAccountInfo_h #endif // hifi_DataServerAccountInfo_h

View file

@ -29,6 +29,7 @@ DomainHandler::DomainHandler(QObject* parent) :
_uuid(), _uuid(),
_sockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)), _sockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)),
_assignmentUUID(), _assignmentUUID(),
_connectionToken(),
_iceDomainID(), _iceDomainID(),
_iceClientID(), _iceClientID(),
_iceServerSockAddr(), _iceServerSockAddr(),
@ -43,6 +44,7 @@ DomainHandler::DomainHandler(QObject* parent) :
void DomainHandler::clearConnectionInfo() { void DomainHandler::clearConnectionInfo() {
_uuid = QUuid(); _uuid = QUuid();
_connectionToken = QUuid();
_icePeer.reset(); _icePeer.reset();

View file

@ -50,6 +50,9 @@ public:
unsigned short getPort() const { return _sockAddr.getPort(); } unsigned short getPort() const { return _sockAddr.getPort(); }
void setPort(quint16 port) { _sockAddr.setPort(port); } void setPort(quint16 port) { _sockAddr.setPort(port); }
const QUuid& getConnectionToken() const { return _connectionToken; }
void setConnectionToken(const QUuid& connectionToken) { _connectionToken = connectionToken; }
const QUuid& getAssignmentUUID() const { return _assignmentUUID; } const QUuid& getAssignmentUUID() const { return _assignmentUUID; }
void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; } void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; }
@ -114,6 +117,7 @@ private:
QString _hostname; QString _hostname;
HifiSockAddr _sockAddr; HifiSockAddr _sockAddr;
QUuid _assignmentUUID; QUuid _assignmentUUID;
QUuid _connectionToken;
QUuid _iceDomainID; QUuid _iceDomainID;
QUuid _iceClientID; QUuid _iceClientID;
HifiSockAddr _iceServerSockAddr; HifiSockAddr _iceServerSockAddr;

View file

@ -94,7 +94,7 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned
packetReceiver.registerListener(PacketType::PingReply, this, "processPingReplyPacket"); packetReceiver.registerListener(PacketType::PingReply, this, "processPingReplyPacket");
packetReceiver.registerListener(PacketType::ICEPing, this, "processICEPingPacket"); packetReceiver.registerListener(PacketType::ICEPing, this, "processICEPingPacket");
packetReceiver.registerListener(PacketType::DomainServerAddedNode, this, "processDomainServerAddedNode"); packetReceiver.registerListener(PacketType::DomainServerAddedNode, this, "processDomainServerAddedNode");
packetReceiver.registerListener(PacketType::DomainServerConnectionToken, this, "processDomainServerConnectionTokenPacket");
packetReceiver.registerListener(PacketType::ICEServerPeerInformation, &_domainHandler, "processICEResponsePacket"); packetReceiver.registerListener(PacketType::ICEServerPeerInformation, &_domainHandler, "processICEResponsePacket");
packetReceiver.registerListener(PacketType::DomainServerRequireDTLS, &_domainHandler, "processDTLSRequirementPacket"); packetReceiver.registerListener(PacketType::DomainServerRequireDTLS, &_domainHandler, "processDTLSRequirementPacket");
packetReceiver.registerListener(PacketType::ICEPingReply, &_domainHandler, "processICEPingReplyPacket"); packetReceiver.registerListener(PacketType::ICEPingReply, &_domainHandler, "processICEPingReplyPacket");
@ -276,17 +276,23 @@ void NodeList::sendDomainServerCheckIn() {
packetStream << _ownerType << _publicSockAddr << _localSockAddr << _nodeTypesOfInterest.toList(); packetStream << _ownerType << _publicSockAddr << _localSockAddr << _nodeTypesOfInterest.toList();
// if this is a connect request, and we can present a username signature, send it along // if this is a connect request, and we can present a username signature, send it along
if (!_domainHandler.isConnected()) { if (!_domainHandler.isConnected() ) {
DataServerAccountInfo& accountInfo = AccountManager::getInstance().getAccountInfo(); DataServerAccountInfo& accountInfo = AccountManager::getInstance().getAccountInfo();
packetStream << accountInfo.getUsername(); packetStream << accountInfo.getUsername();
const QByteArray& usernameSignature = AccountManager::getInstance().getAccountInfo().getUsernameSignature(); // get connection token from the domain-server
const QUuid& connectionToken = _domainHandler.getConnectionToken();
if (!connectionToken.isNull()) {
const QByteArray& usernameSignature = AccountManager::getInstance().getAccountInfo().getUsernameSignature(connectionToken);
if (!usernameSignature.isEmpty()) { if (!usernameSignature.isEmpty()) {
qCDebug(networking) << "Including username signature in domain connect request.";
packetStream << usernameSignature; packetStream << usernameSignature;
} }
} }
}
flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendDSCheckIn); flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendDSCheckIn);
@ -361,6 +367,7 @@ void NodeList::sendDSPathQuery(const QString& newPath) {
} }
} }
void NodeList::processDomainServerPathResponse(QSharedPointer<NLPacket> packet) { void NodeList::processDomainServerPathResponse(QSharedPointer<NLPacket> packet) {
// This is a response to a path query we theoretically made. // This is a response to a path query we theoretically made.
// In the future we may want to check that this was actually from our DS and for a query we actually made. // In the future we may want to check that this was actually from our DS and for a query we actually made.
@ -450,6 +457,17 @@ void NodeList::pingPunchForDomainServer() {
} }
} }
void NodeList::processDomainServerConnectionTokenPacket(QSharedPointer<NLPacket> packet) {
if (_domainHandler.getSockAddr().isNull()) {
// refuse to process this packet if we aren't currently connected to the DS
return;
}
// read in the connection token from the packet, then send domain-server checkin
_domainHandler.setConnectionToken(QUuid::fromRfc4122(packet->read(NUM_BYTES_RFC4122_UUID)));
sendDomainServerCheckIn();
}
void NodeList::processDomainServerList(QSharedPointer<NLPacket> packet) { void NodeList::processDomainServerList(QSharedPointer<NLPacket> packet) {
if (_domainHandler.getSockAddr().isNull()) { if (_domainHandler.getSockAddr().isNull()) {
// refuse to process this packet if we aren't currently connected to the DS // refuse to process this packet if we aren't currently connected to the DS

View file

@ -76,6 +76,8 @@ public slots:
void processDomainServerAddedNode(QSharedPointer<NLPacket> packet); void processDomainServerAddedNode(QSharedPointer<NLPacket> packet);
void processDomainServerPathResponse(QSharedPointer<NLPacket> packet); void processDomainServerPathResponse(QSharedPointer<NLPacket> packet);
void processDomainServerConnectionTokenPacket(QSharedPointer<NLPacket> packet);
void processPingPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode); void processPingPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);
void processPingReplyPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode); void processPingReplyPacket(QSharedPointer<NLPacket> packet, SharedNodePointer sendingNode);

View file

@ -26,7 +26,7 @@ const QSet<PacketType::Value> SEQUENCE_NUMBERED_PACKETS = QSet<PacketType::Value
const QSet<PacketType::Value> NON_SOURCED_PACKETS = QSet<PacketType::Value>() const QSet<PacketType::Value> NON_SOURCED_PACKETS = QSet<PacketType::Value>()
<< StunResponse << CreateAssignment << RequestAssignment << StunResponse << CreateAssignment << RequestAssignment
<< DomainServerRequireDTLS << DomainConnectRequest << DomainServerRequireDTLS << DomainConnectRequest << DomainServerConnectionToken
<< DomainList << DomainConnectionDenied << DomainList << DomainConnectionDenied
<< DomainServerPathQuery << DomainServerPathResponse << DomainServerPathQuery << DomainServerPathResponse
<< DomainServerAddedNode << DomainServerAddedNode
@ -119,6 +119,7 @@ QString nameForPacketType(PacketType::Value packetType) {
PACKET_TYPE_NAME_LOOKUP(ICEPingReply); PACKET_TYPE_NAME_LOOKUP(ICEPingReply);
PACKET_TYPE_NAME_LOOKUP(EntityAdd); PACKET_TYPE_NAME_LOOKUP(EntityAdd);
PACKET_TYPE_NAME_LOOKUP(EntityEdit); PACKET_TYPE_NAME_LOOKUP(EntityEdit);
PACKET_TYPE_NAME_LOOKUP(DomainServerConnectionToken);
default: default:
return QString("Type: ") + QString::number((int)packetType); return QString("Type: ") + QString::number((int)packetType);
} }

View file

@ -73,7 +73,8 @@ namespace PacketType {
EntityQuery, EntityQuery,
EntityAdd, EntityAdd,
EntityErase, EntityErase,
EntityEdit EntityEdit,
DomainServerConnectionToken
}; };
}; };

View file

@ -636,8 +636,6 @@ OctreeElement* OctreeElement::getOrCreateChildElementAt(float x, float y, float
if (s > halfOurScale) { if (s > halfOurScale) {
return this; return this;
} }
// otherwise, we need to find which of our children we should recurse
glm::vec3 ourCenter = _cube.calcCenter();
int childIndex = getMyChildContainingPoint(glm::vec3(x, y, z)); int childIndex = getMyChildContainingPoint(glm::vec3(x, y, z));

View file

@ -21,6 +21,23 @@ uniform float glowIntensity;
// the alpha threshold // the alpha threshold
uniform float alphaThreshold; uniform float alphaThreshold;
uniform sampler2D normalFittingMap;
vec3 bestFitNormal(vec3 normal) {
vec3 absNorm = abs(normal);
float maxNAbs = max(absNorm.z, max(absNorm.x, absNorm.y));
vec2 texcoord = (absNorm.z < maxNAbs ?
(absNorm.y < maxNAbs ? absNorm.yz : absNorm.xz) :
absNorm.xy);
texcoord = (texcoord.x < texcoord.y ? texcoord.yx : texcoord.xy);
texcoord.y /= texcoord.x;
vec3 cN = normal / maxNAbs;
float fittingScale = texture(normalFittingMap, texcoord).a;
cN *= fittingScale;
return (cN * 0.5 + 0.5);
}
float evalOpaqueFinalAlpha(float alpha, float mapAlpha) { float evalOpaqueFinalAlpha(float alpha, float mapAlpha) {
return mix(alpha * glowIntensity, 1.0 - alpha * glowIntensity, step(mapAlpha, alphaThreshold)); return mix(alpha * glowIntensity, 1.0 - alpha * glowIntensity, step(mapAlpha, alphaThreshold));
} }
@ -33,7 +50,7 @@ void packDeferredFragment(vec3 normal, float alpha, vec3 diffuse, vec3 specular,
discard; discard;
} }
_fragColor0 = vec4(diffuse.rgb, alpha); _fragColor0 = vec4(diffuse.rgb, alpha);
_fragColor1 = vec4(normal, 0.0) * 0.5 + vec4(0.5, 0.5, 0.5, 1.0); _fragColor1 = vec4(bestFitNormal(normal), 1.0);
_fragColor2 = vec4(specular, shininess / 128.0); _fragColor2 = vec4(specular, shininess / 128.0);
} }
@ -44,7 +61,7 @@ void packDeferredFragmentLightmap(vec3 normal, float alpha, vec3 diffuse, vec3 s
_fragColor0 = vec4(diffuse.rgb, alpha); _fragColor0 = vec4(diffuse.rgb, alpha);
//_fragColor1 = vec4(normal, 0.0) * 0.5 + vec4(0.5, 0.5, 0.5, 1.0); //_fragColor1 = vec4(normal, 0.0) * 0.5 + vec4(0.5, 0.5, 0.5, 1.0);
_fragColor1 = vec4(normal, 0.0) * 0.5 + vec4(0.5, 0.5, 0.5, 0.5); _fragColor1 = vec4(bestFitNormal(normal), 0.5);
_fragColor2 = vec4(emissive, shininess / 128.0); _fragColor2 = vec4(emissive, shininess / 128.0);
} }

View file

@ -86,6 +86,7 @@ void DeferredLightingEffect::init(AbstractViewStateInterface* viewState) {
_emissiveShader = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PSEmissive)); _emissiveShader = gpu::ShaderPointer(gpu::Shader::createProgram(VS, PSEmissive));
gpu::Shader::BindingSet slotBindings; gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), DeferredLightingEffect::NORMAL_FITTING_MAP_SLOT));
gpu::Shader::makeProgram(*_simpleShader, slotBindings); gpu::Shader::makeProgram(*_simpleShader, slotBindings);
gpu::Shader::makeProgram(*_emissiveShader, slotBindings); gpu::Shader::makeProgram(*_emissiveShader, slotBindings);
@ -151,6 +152,8 @@ void DeferredLightingEffect::bindSimpleProgram(gpu::Batch& batch, bool textured,
// If it is not textured, bind white texture and keep using textured pipeline // If it is not textured, bind white texture and keep using textured pipeline
batch.setResourceTexture(0, DependencyManager::get<TextureCache>()->getWhiteTexture()); batch.setResourceTexture(0, DependencyManager::get<TextureCache>()->getWhiteTexture());
} }
batch.setResourceTexture(NORMAL_FITTING_MAP_SLOT, DependencyManager::get<TextureCache>()->getNormalFittingTexture());
} }
void DeferredLightingEffect::renderSolidSphere(gpu::Batch& batch, float radius, int slices, int stacks, const glm::vec4& color) { void DeferredLightingEffect::renderSolidSphere(gpu::Batch& batch, float radius, int slices, int stacks, const glm::vec4& color) {

View file

@ -30,6 +30,7 @@ class DeferredLightingEffect : public Dependency {
SINGLETON_DEPENDENCY SINGLETON_DEPENDENCY
public: public:
static const int NORMAL_FITTING_MAP_SLOT = 10;
void init(AbstractViewStateInterface* viewState); void init(AbstractViewStateInterface* viewState);

View file

@ -20,7 +20,6 @@
#include <FSTReader.h> #include <FSTReader.h>
#include <NumericalConstants.h> #include <NumericalConstants.h>
#include <gpu/Batch.h>
#include <gpu/GLBackend.h> #include <gpu/GLBackend.h>
#include "TextureCache.h" #include "TextureCache.h"
@ -2240,10 +2239,17 @@ bool NetworkMeshPart::isTranslucent() const {
return diffuseTexture && diffuseTexture->isTranslucent(); return diffuseTexture && diffuseTexture->isTranslucent();
} }
bool NetworkMesh::isPartTranslucent(const FBXMesh& fbxMesh, int partIndex) const {
assert(partIndex >= 0);
assert(partIndex < parts.size());
return (parts.at(partIndex).isTranslucent() || fbxMesh.parts.at(partIndex).opacity != 1.0f);
}
int NetworkMesh::getTranslucentPartCount(const FBXMesh& fbxMesh) const { int NetworkMesh::getTranslucentPartCount(const FBXMesh& fbxMesh) const {
int count = 0; int count = 0;
for (int i = 0; i < parts.size(); i++) { for (int i = 0; i < parts.size(); i++) {
if (parts.at(i).isTranslucent() || fbxMesh.parts.at(i).opacity != 1.0f) { if (isPartTranslucent(fbxMesh, i)) {
count++; count++;
} }
} }

View file

@ -398,6 +398,7 @@ public:
QVector<NetworkMeshPart> parts; QVector<NetworkMeshPart> parts;
int getTranslucentPartCount(const FBXMesh& fbxMesh) const; int getTranslucentPartCount(const FBXMesh& fbxMesh) const;
bool isPartTranslucent(const FBXMesh& fbxMesh, int partIndex) const;
}; };
#endif // hifi_GeometryCache_h #endif // hifi_GeometryCache_h

View file

@ -105,6 +105,8 @@ void Model::RenderPipelineLib::addRenderPipeline(Model::RenderKey key,
slotBindings.insert(gpu::Shader::Binding(std::string("specularMap"), 2)); slotBindings.insert(gpu::Shader::Binding(std::string("specularMap"), 2));
slotBindings.insert(gpu::Shader::Binding(std::string("emissiveMap"), 3)); slotBindings.insert(gpu::Shader::Binding(std::string("emissiveMap"), 3));
slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), 4)); slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), 4));
slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), DeferredLightingEffect::NORMAL_FITTING_MAP_SLOT));
gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vertexShader, pixelShader)); gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vertexShader, pixelShader));
gpu::Shader::makeProgram(*program, slotBindings); gpu::Shader::makeProgram(*program, slotBindings);
@ -180,6 +182,8 @@ void Model::RenderPipelineLib::initLocations(gpu::ShaderPointer& program, Model:
locations.emissiveParams = program->getUniforms().findLocation("emissiveParams"); locations.emissiveParams = program->getUniforms().findLocation("emissiveParams");
locations.glowIntensity = program->getUniforms().findLocation("glowIntensity"); locations.glowIntensity = program->getUniforms().findLocation("glowIntensity");
locations.normalFittingMapUnit = program->getTextures().findLocation("normalFittingMap");
locations.specularTextureUnit = program->getTextures().findLocation("specularMap"); locations.specularTextureUnit = program->getTextures().findLocation("specularMap");
locations.emissiveTextureUnit = program->getTextures().findLocation("emissiveMap"); locations.emissiveTextureUnit = program->getTextures().findLocation("emissiveMap");
@ -1820,11 +1824,10 @@ void Model::segregateMeshGroups() {
translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false; translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false;
} }
// Debug... // Create the render payloads
int totalParts = mesh.parts.size(); int totalParts = mesh.parts.size();
for (int partIndex = 0; partIndex < totalParts; partIndex++) { for (int partIndex = 0; partIndex < totalParts; partIndex++) {
// this is a good place to create our renderPayloads if (networkMesh.isPartTranslucent(mesh, partIndex)) {
if (translucentMesh) {
_transparentRenderItems << std::make_shared<MeshPartPayload>(true, this, i, partIndex); _transparentRenderItems << std::make_shared<MeshPartPayload>(true, this, i, partIndex);
} else { } else {
_opaqueRenderItems << std::make_shared<MeshPartPayload>(false, this, i, partIndex); _opaqueRenderItems << std::make_shared<MeshPartPayload>(false, this, i, partIndex);
@ -1864,6 +1867,10 @@ void Model::pickPrograms(gpu::Batch& batch, RenderMode mode, bool translucent, f
const float DEFAULT_GLOW_INTENSITY = 1.0f; // FIXME - glow is removed const float DEFAULT_GLOW_INTENSITY = 1.0f; // FIXME - glow is removed
batch._glUniform1f(locations->glowIntensity, DEFAULT_GLOW_INTENSITY); batch._glUniform1f(locations->glowIntensity, DEFAULT_GLOW_INTENSITY);
} }
if ((locations->normalFittingMapUnit > -1)) {
batch.setResourceTexture(locations->normalFittingMapUnit, DependencyManager::get<TextureCache>()->getNormalFittingTexture());
}
} }
bool Model::initWhenReady(render::ScenePointer scene) { bool Model::initWhenReady(render::ScenePointer scene) {

View file

@ -350,6 +350,7 @@ private:
int emissiveTextureUnit; int emissiveTextureUnit;
int emissiveParams; int emissiveParams;
int glowIntensity; int glowIntensity;
int normalFittingMapUnit;
int materialBufferUnit; int materialBufferUnit;
int clusterMatrices; int clusterMatrices;
int clusterIndices; int clusterIndices;

View file

@ -21,6 +21,7 @@
#include <QRunnable> #include <QRunnable>
#include <QThreadPool> #include <QThreadPool>
#include <qimagereader.h> #include <qimagereader.h>
#include "PathUtils.h"
#include <gpu/Batch.h> #include <gpu/Batch.h>
@ -129,6 +130,14 @@ const gpu::TexturePointer& TextureCache::getBlackTexture() {
return _blackTexture; return _blackTexture;
} }
const gpu::TexturePointer& TextureCache::getNormalFittingTexture() {
if (!_normalFittingTexture) {
_normalFittingTexture = getImageTexture(PathUtils::resourcesPath() + "images/normalFittingScale.dds");
}
return _normalFittingTexture;
}
/// Extra data for creating textures. /// Extra data for creating textures.
class TextureExtra { class TextureExtra {
public: public:
@ -170,6 +179,7 @@ gpu::TexturePointer TextureCache::getImageTexture(const QString& path) {
return texture; return texture;
} }
QSharedPointer<Resource> TextureCache::createResource(const QUrl& url, QSharedPointer<Resource> TextureCache::createResource(const QUrl& url,
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) { const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
const TextureExtra* textureExtra = static_cast<const TextureExtra*>(extra); const TextureExtra* textureExtra = static_cast<const TextureExtra*>(extra);

View file

@ -54,6 +54,9 @@ public:
/// Returns the a black texture (useful for a default). /// Returns the a black texture (useful for a default).
const gpu::TexturePointer& getBlackTexture(); const gpu::TexturePointer& getBlackTexture();
// Returns a map used to compress the normals through a fitting scale algorithm
const gpu::TexturePointer& getNormalFittingTexture();
/// Returns a texture version of an image file /// Returns a texture version of an image file
static gpu::TexturePointer getImageTexture(const QString& path); static gpu::TexturePointer getImageTexture(const QString& path);
@ -76,6 +79,7 @@ private:
gpu::TexturePointer _grayTexture; gpu::TexturePointer _grayTexture;
gpu::TexturePointer _blueTexture; gpu::TexturePointer _blueTexture;
gpu::TexturePointer _blackTexture; gpu::TexturePointer _blackTexture;
gpu::TexturePointer _normalFittingTexture;
QHash<QUrl, QWeakPointer<NetworkTexture> > _dilatableNetworkTextures; QHash<QUrl, QWeakPointer<NetworkTexture> > _dilatableNetworkTextures;
}; };

View file

@ -9,37 +9,21 @@
// //
#include <iostream> #include <iostream>
#include <memory> #include <string>
#include <mutex> #include <vector>
#include <unordered_map>
#include <QWindow>
#include <QtGlobal>
#include <QFile>
#include <QImage>
#include <QLoggingCategory>
#include <gpu/Context.h>
#include <gpu/GLBackend.h> #include <gpu/GLBackend.h>
#include <QOpenGLBuffer>
#include <QOpenGLContext> #include <QOpenGLContext>
#include <QOpenGLDebugLogger> #include <QOpenGLDebugLogger>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture> #include <QDir>
#include <QOpenGLVertexArrayObject> #include <QElapsedTimer>
#include <QGuiApplication>
#include <QLoggingCategory>
#include <QResizeEvent> #include <QResizeEvent>
#include <QTime>
#include <QTimer> #include <QTimer>
#include <QWindow> #include <QWindow>
#include <QElapsedTimer>
#include <QDir>
#include <QGuiApplication>
#include <PathUtils.h>
#include "gpu/Batch.h"
#include "gpu/Context.h"
class RateCounter { class RateCounter {
std::vector<float> times; std::vector<float> times;
@ -71,7 +55,7 @@ public:
float rate() const { float rate() const {
if (elapsed() == 0.0f) { if (elapsed() == 0.0f) {
return NAN; return 0.0f;
} }
return (float) count() / elapsed(); return (float) count() / elapsed();
} }

View file

@ -6,39 +6,20 @@
// //
#include <iostream> #include <iostream>
#include <memory>
#include <mutex> #include <mutex>
#include <unordered_map>
#include <QWindow>
#include <QtGlobal>
#include <QFile>
#include <QImage>
#include <QLoggingCategory>
#include <gpu/Context.h>
#include <gpu/GLBackend.h> #include <gpu/GLBackend.h>
#include <QOpenGLBuffer>
#include <QOpenGLContext>
#include <QOpenGLDebugLogger> #include <QOpenGLDebugLogger>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture> #include <QLoggingCategory>
#include <QOpenGLVertexArrayObject>
#include <QResizeEvent> #include <QResizeEvent>
#include <QTime>
#include <QTimer> #include <QTimer>
#include <QWindow> #include <QWindow>
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QDir> #include <QDir>
#include <QGuiApplication> #include <QGuiApplication>
#include <PathUtils.h>
#include "gpu/Batch.h"
#include "gpu/Context.h"
#include "../model/Skybox_vert.h" #include "../model/Skybox_vert.h"
#include "../model/Skybox_frag.h" #include "../model/Skybox_frag.h"
@ -159,7 +140,7 @@ public:
float rate() const { float rate() const {
if (elapsed() == 0.0f) { if (elapsed() == 0.0f) {
return NAN; return 0.0f;
} }
return (float) count() / elapsed(); return (float) count() / elapsed();
} }

View file

@ -68,7 +68,7 @@ public:
float rate() const { float rate() const {
if (elapsed() == 0.0f) { if (elapsed() == 0.0f) {
return NAN; return 0.0f;
} }
return (float) count() / elapsed(); return (float) count() / elapsed();
} }