Merge branch 'master' of https://github.com/highfidelity/hifi into blackProp

This commit is contained in:
Sam Gateau 2019-04-16 18:43:30 -07:00
commit f6001c178d
16 changed files with 227 additions and 168 deletions

View file

@ -498,8 +498,10 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar
// on the creation of entities for that avatar instance and the deletion of entities for this instance
avatar->removeAvatarEntitiesFromTree();
if (removalReason != KillAvatarReason::AvatarDisconnected) {
emit AvatarInputs::getInstance()->avatarEnteredIgnoreRadius(avatar->getSessionUUID());
emit DependencyManager::get<UsersScriptingInterface>()->enteredIgnoreRadius();
if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble) {
emit AvatarInputs::getInstance()->avatarEnteredIgnoreRadius(avatar->getSessionUUID());
emit DependencyManager::get<UsersScriptingInterface>()->enteredIgnoreRadius();
}
workload::Transaction workloadTransaction;
workloadTransaction.remove(avatar->getSpaceIndex());

View file

@ -3172,17 +3172,40 @@ int MyAvatar::sendAvatarDataPacket(bool sendAll) {
return bytesSent;
}
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.47f;
bool MyAvatar::cameraInsideHead(const glm::vec3& cameraPosition) const {
if (!_skeletonModel) {
return false;
}
// transform cameraPosition into rig coordinates
AnimPose rigToWorld = AnimPose(getWorldOrientation() * Quaternions::Y_180, getWorldPosition());
AnimPose worldToRig = rigToWorld.inverse();
glm::vec3 rigCameraPosition = worldToRig * cameraPosition;
// use head k-dop shape to determine if camera is inside head.
const Rig& rig = _skeletonModel->getRig();
int headJointIndex = rig.indexOfJoint("Head");
if (headJointIndex >= 0) {
const HFMModel& hfmModel = _skeletonModel->getHFMModel();
AnimPose headPose;
if (rig.getAbsoluteJointPoseInRigFrame(headJointIndex, headPose)) {
glm::vec3 displacement;
const HFMJointShapeInfo& headShapeInfo = hfmModel.joints[headJointIndex].shapeInfo;
return findPointKDopDisplacement(rigCameraPosition, headPose, headShapeInfo, displacement);
}
}
// fall back to simple distance check.
const float RENDER_HEAD_CUTOFF_DISTANCE = 0.47f;
return glm::length(cameraPosition - getHeadPosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getModelScale());
}
bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const {
bool defaultMode = renderArgs->_renderMode == RenderArgs::DEFAULT_RENDER_MODE;
bool firstPerson = qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON;
bool overrideAnim = _skeletonModel ? _skeletonModel->getRig().isPlayingOverrideAnimation() : false;
bool insideHead = cameraInsideHead(renderArgs->getViewFrustum().getPosition());
return !defaultMode || !firstPerson || !insideHead;
return !defaultMode || (!firstPerson && !insideHead) || (overrideAnim && !insideHead);
}
void MyAvatar::setHasScriptedBlendshapes(bool hasScriptedBlendshapes) {
@ -4798,7 +4821,12 @@ bool MyAvatar::isReadyForPhysics() const {
}
void MyAvatar::setSprintMode(bool sprint) {
_walkSpeedScalar = sprint ? AVATAR_SPRINT_SPEED_SCALAR : AVATAR_WALK_SPEED_SCALAR;
if (qApp->isHMDMode()) {
_walkSpeedScalar = sprint ? AVATAR_DESKTOP_SPRINT_SPEED_SCALAR : AVATAR_WALK_SPEED_SCALAR;
}
else {
_walkSpeedScalar = sprint ? AVATAR_HMD_SPRINT_SPEED_SCALAR : AVATAR_WALK_SPEED_SCALAR;
}
}
void MyAvatar::setIsInWalkingState(bool isWalking) {

View file

@ -96,28 +96,32 @@ int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) {
}
}
EC_KEY* readKeys(const char* filename) {
FILE* fp;
EC_KEY *key = NULL;
if ((fp = fopen(filename, "rt"))) {
EC_KEY* readKeys(QString filename) {
QFile file(filename);
EC_KEY* key = NULL;
if (file.open(QFile::ReadOnly)) {
// file opened successfully
qCDebug(commerce) << "opened key file" << filename;
if ((key = PEM_read_EC_PUBKEY(fp, NULL, NULL, NULL))) {
QByteArray pemKeyBytes = file.readAll();
BIO* bufio = BIO_new_mem_buf((void*)pemKeyBytes.constData(), pemKeyBytes.length());
if ((key = PEM_read_bio_EC_PUBKEY(bufio, NULL, NULL, NULL))) {
// now read private key
qCDebug(commerce) << "read public key";
if ((key = PEM_read_ECPrivateKey(fp, &key, passwordCallback, NULL))) {
if ((key = PEM_read_bio_ECPrivateKey(bufio, &key, passwordCallback, NULL))) {
qCDebug(commerce) << "read private key";
fclose(fp);
return key;
BIO_free(bufio);
file.close();
} else {
qCDebug(commerce) << "failed to read private key";
}
qCDebug(commerce) << "failed to read private key";
} else {
qCDebug(commerce) << "failed to read public key";
}
fclose(fp);
BIO_free(bufio);
file.close();
} else {
qCDebug(commerce) << "failed to open key file" << filename;
}
@ -131,8 +135,7 @@ bool Wallet::writeBackupInstructions() {
QFile outputFile(outputFilename);
bool retval = false;
if (getKeyFilePath().isEmpty())
{
if (getKeyFilePath().isEmpty()) {
return false;
}
@ -152,7 +155,7 @@ bool Wallet::writeBackupInstructions() {
outputFile.write(text.toUtf8());
// Close the output file
outputFile.close();
outputFile.close();
retval = true;
qCDebug(commerce) << "wrote html file successfully";
@ -165,28 +168,35 @@ bool Wallet::writeBackupInstructions() {
return retval;
}
bool writeKeys(const char* filename, EC_KEY* keys) {
FILE* fp;
bool writeKeys(QString filename, EC_KEY* keys) {
BIO* bio = BIO_new(BIO_s_mem());
bool retval = false;
if ((fp = fopen(filename, "wt"))) {
if (!PEM_write_EC_PUBKEY(fp, keys)) {
fclose(fp);
qCCritical(commerce) << "failed to write public key";
return retval;
}
if (!PEM_write_bio_EC_PUBKEY(bio, keys)) {
BIO_free(bio);
qCCritical(commerce) << "failed to write public key";
return retval;
}
if (!PEM_write_ECPrivateKey(fp, keys, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) {
fclose(fp);
qCCritical(commerce) << "failed to write private key";
return retval;
}
if (!PEM_write_bio_ECPrivateKey(bio, keys, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) {
BIO_free(bio);
qCCritical(commerce) << "failed to write private key";
return retval;
}
QFile file(filename);
if (!file.open(QIODevice::WriteOnly)) {
const char* bio_data;
long bio_size = BIO_get_mem_data(bio, &bio_data);
QByteArray keyBytes(bio_data, bio_size);
file.write(keyBytes);
retval = true;
qCDebug(commerce) << "wrote keys successfully";
fclose(fp);
file.close();
} else {
qCDebug(commerce) << "failed to open key file" << filename;
}
BIO_free(bio);
return retval;
}
@ -215,7 +225,6 @@ QByteArray Wallet::getWallet() {
}
QPair<QByteArray*, QByteArray*> generateECKeypair() {
EC_KEY* keyPair = EC_KEY_new_by_curve_name(NID_secp256k1);
QPair<QByteArray*, QByteArray*> retval{};
@ -235,7 +244,6 @@ QPair<QByteArray*, QByteArray*> generateECKeypair() {
if (publicKeyLength <= 0 || privateKeyLength <= 0) {
qCDebug(commerce) << "Error getting DER public or private key from EC struct -" << ERR_get_error();
// cleanup the EC struct
EC_KEY_free(keyPair);
@ -251,8 +259,7 @@ QPair<QByteArray*, QByteArray*> generateECKeypair() {
return retval;
}
if (!writeKeys(keyFilePath().toStdString().c_str(), keyPair)) {
if (!writeKeys(keyFilePath(), keyPair)) {
qCDebug(commerce) << "couldn't save keys!";
return retval;
}
@ -273,13 +280,18 @@ QPair<QByteArray*, QByteArray*> generateECKeypair() {
// END copied code (which will soon change)
// the public key can just go into a byte array
QByteArray readPublicKey(const char* filename) {
FILE* fp;
EC_KEY* key = NULL;
if ((fp = fopen(filename, "r"))) {
QByteArray readPublicKey(QString filename) {
QByteArray retval;
QFile file(filename);
if (file.open(QIODevice::ReadOnly)) {
// file opened successfully
qCDebug(commerce) << "opened key file" << filename;
if ((key = PEM_read_EC_PUBKEY(fp, NULL, NULL, NULL))) {
QByteArray pemKeyBytes = file.readAll();
BIO* bufio = BIO_new_mem_buf((void*)pemKeyBytes.constData(), pemKeyBytes.length());
EC_KEY* key = PEM_read_bio_EC_PUBKEY(bufio, NULL, NULL, NULL);
if (key) {
// file read successfully
unsigned char* publicKeyDER = NULL;
int publicKeyLength = i2d_EC_PUBKEY(key, &publicKeyDER);
@ -287,17 +299,19 @@ QByteArray readPublicKey(const char* filename) {
// cleanup
EC_KEY_free(key);
fclose(fp);
qCDebug(commerce) << "parsed public key file successfully";
QByteArray retval((char*)publicKeyDER, publicKeyLength);
OPENSSL_free(publicKeyDER);
BIO_free(bufio);
file.close();
return retval;
} else {
qCDebug(commerce) << "couldn't parse" << filename;
}
fclose(fp);
BIO_free(bufio);
file.close();
} else {
qCDebug(commerce) << "couldn't open" << filename;
}
@ -306,13 +320,17 @@ QByteArray readPublicKey(const char* filename) {
// the private key should be read/copied into heap memory. For now, we need the EC_KEY struct
// so I'll return that.
EC_KEY* readPrivateKey(const char* filename) {
FILE* fp;
EC_KEY* readPrivateKey(QString filename) {
QFile file(filename);
EC_KEY* key = NULL;
if ((fp = fopen(filename, "r"))) {
if (file.open(QIODevice::ReadOnly)) {
// file opened successfully
qCDebug(commerce) << "opened key file" << filename;
if ((key = PEM_read_ECPrivateKey(fp, &key, passwordCallback, NULL))) {
QByteArray pemKeyBytes = file.readAll();
BIO* bufio = BIO_new_mem_buf((void*)pemKeyBytes.constData(), pemKeyBytes.length());
if ((key = PEM_read_bio_ECPrivateKey(bufio, &key, passwordCallback, NULL))) {
qCDebug(commerce) << "parsed private key file successfully";
} else {
@ -320,7 +338,8 @@ EC_KEY* readPrivateKey(const char* filename) {
// if the passphrase is wrong, then let's not cache it
DependencyManager::get<Wallet>()->setPassphrase("");
}
fclose(fp);
BIO_free(bufio);
file.close();
} else {
qCDebug(commerce) << "couldn't open" << filename;
}
@ -361,7 +380,7 @@ Wallet::Wallet() {
if (wallet->getKeyFilePath().isEmpty() || !wallet->getSecurityImage()) {
if (keyStatus == "preexisting") {
status = (uint) WalletStatus::WALLET_STATUS_PREEXISTING;
} else{
} else {
status = (uint) WalletStatus::WALLET_STATUS_NOT_SET_UP;
}
} else if (!wallet->walletIsAuthenticatedWithPassphrase()) {
@ -371,7 +390,6 @@ Wallet::Wallet() {
} else {
status = (uint) WalletStatus::WALLET_STATUS_READY;
}
walletScriptingInterface->setWalletStatus(status);
});
@ -569,10 +587,10 @@ bool Wallet::walletIsAuthenticatedWithPassphrase() {
}
// otherwise, we have a passphrase but no keys, so we have to check
auto publicKey = readPublicKey(keyFilePath().toStdString().c_str());
auto publicKey = readPublicKey(keyFilePath());
if (publicKey.size() > 0) {
if (auto key = readPrivateKey(keyFilePath().toStdString().c_str())) {
if (auto key = readPrivateKey(keyFilePath())) {
EC_KEY_free(key);
// be sure to add the public key so we don't do this over and over
@ -631,8 +649,7 @@ QStringList Wallet::listPublicKeys() {
QString Wallet::signWithKey(const QByteArray& text, const QString& key) {
EC_KEY* ecPrivateKey = NULL;
auto keyFilePathString = keyFilePath().toStdString();
if ((ecPrivateKey = readPrivateKey(keyFilePath().toStdString().c_str()))) {
if ((ecPrivateKey = readPrivateKey(keyFilePath()))) {
unsigned char* sig = new unsigned char[ECDSA_size(ecPrivateKey)];
unsigned int signatureBytes = 0;
@ -641,12 +658,8 @@ QString Wallet::signWithKey(const QByteArray& text, const QString& key) {
QByteArray hashedPlaintext = QCryptographicHash::hash(text, QCryptographicHash::Sha256);
int retrn = ECDSA_sign(0,
reinterpret_cast<const unsigned char*>(hashedPlaintext.constData()),
hashedPlaintext.size(),
sig,
&signatureBytes, ecPrivateKey);
int retrn = ECDSA_sign(0, reinterpret_cast<const unsigned char*>(hashedPlaintext.constData()), hashedPlaintext.size(),
sig, &signatureBytes, ecPrivateKey);
EC_KEY_free(ecPrivateKey);
QByteArray signature(reinterpret_cast<const char*>(sig), signatureBytes);
@ -682,7 +695,6 @@ void Wallet::updateImageProvider() {
}
void Wallet::chooseSecurityImage(const QString& filename) {
if (_securityImage) {
delete _securityImage;
}
@ -754,7 +766,7 @@ QString Wallet::getKeyFilePath() {
}
bool Wallet::writeWallet(const QString& newPassphrase) {
EC_KEY* keys = readKeys(keyFilePath().toStdString().c_str());
EC_KEY* keys = readKeys(keyFilePath());
auto ledger = DependencyManager::get<Ledger>();
// Remove any existing locker, because it will be out of date.
if (!_publicKeys.isEmpty() && !ledger->receiveAt(_publicKeys.first(), _publicKeys.first(), QByteArray())) {
@ -768,7 +780,7 @@ bool Wallet::writeWallet(const QString& newPassphrase) {
setPassphrase(newPassphrase);
}
if (writeKeys(tempFileName.toStdString().c_str(), keys)) {
if (writeKeys(tempFileName, keys)) {
if (writeSecurityImage(_securityImage, tempFileName)) {
// ok, now move the temp file to the correct spot
QFile(QString(keyFilePath())).remove();
@ -834,10 +846,10 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer<ReceivedMessage> pack
challengingNodeUUID = packet->read(challengingNodeUUIDByteArraySize);
}
EC_KEY* ec = readKeys(keyFilePath().toStdString().c_str());
EC_KEY* ec = readKeys(keyFilePath());
QString sig;
if (ec) {
if (ec) {
ERR_clear_error();
sig = signWithKey(text, ""); // base64 signature, QByteArray cast (on return) to QString FIXME should pass ec as string so we can tell which key to sign with
status = 1;

View file

@ -142,3 +142,72 @@ glm::quat computeBodyFacingFromHead(const glm::quat& headRot, const glm::vec3& u
return glmExtractRotation(bodyMat);
}
const float INV_SQRT_3 = 1.0f / sqrtf(3.0f);
const int DOP14_COUNT = 14;
const glm::vec3 DOP14_NORMALS[DOP14_COUNT] = {
Vectors::UNIT_X,
-Vectors::UNIT_X,
Vectors::UNIT_Y,
-Vectors::UNIT_Y,
Vectors::UNIT_Z,
-Vectors::UNIT_Z,
glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3),
-glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3),
glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3),
-glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3),
glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3),
-glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3),
glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3),
-glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3)
};
// returns true if the given point lies inside of the k-dop, specified by shapeInfo & shapePose.
// if the given point does lie within the k-dop, it also returns the amount of displacement necessary to push that point outward
// such that it lies on the surface of the kdop.
bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose, const HFMJointShapeInfo& shapeInfo, glm::vec3& displacementOut) {
// transform point into local space of jointShape.
glm::vec3 localPoint = shapePose.inverse().xformPoint(point);
// Only works for 14-dop shape infos.
if (shapeInfo.dots.size() != DOP14_COUNT) {
return false;
}
glm::vec3 minDisplacement(FLT_MAX);
float minDisplacementLen = FLT_MAX;
glm::vec3 p = localPoint - shapeInfo.avgPoint;
float pLen = glm::length(p);
if (pLen > 0.0f) {
int slabCount = 0;
for (int i = 0; i < DOP14_COUNT; i++) {
float dot = glm::dot(p, DOP14_NORMALS[i]);
if (dot > 0.0f && dot < shapeInfo.dots[i]) {
slabCount++;
float distToPlane = pLen * (shapeInfo.dots[i] / dot);
float displacementLen = distToPlane - pLen;
// keep track of the smallest displacement
if (displacementLen < minDisplacementLen) {
minDisplacementLen = displacementLen;
minDisplacement = (p / pLen) * displacementLen;
}
}
}
if (slabCount == (DOP14_COUNT / 2) && minDisplacementLen != FLT_MAX) {
// we are within the k-dop so push the point along the minimum displacement found
displacementOut = shapePose.xformVectorFast(minDisplacement);
return true;
} else {
// point is outside of kdop
return false;
}
} else {
// point is directly on top of shapeInfo.avgPoint.
// push the point out along the x axis.
displacementOut = shapePose.xformVectorFast(shapeInfo.points[0]);
return true;
}
}

View file

@ -128,4 +128,10 @@ protected:
bool _snapshotValid { false };
};
// returns true if the given point lies inside of the k-dop, specified by shapeInfo & shapePose.
// if the given point does lie within the k-dop, it also returns the amount of displacement necessary to push that point outward
// such that it lies on the surface of the kdop.
bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose, const HFMJointShapeInfo& shapeInfo, glm::vec3& displacementOut);
#endif

View file

@ -1521,74 +1521,6 @@ void Rig::updateHead(bool headEnabled, bool hipsEnabled, const AnimPose& headPos
}
}
const float INV_SQRT_3 = 1.0f / sqrtf(3.0f);
const int DOP14_COUNT = 14;
const glm::vec3 DOP14_NORMALS[DOP14_COUNT] = {
Vectors::UNIT_X,
-Vectors::UNIT_X,
Vectors::UNIT_Y,
-Vectors::UNIT_Y,
Vectors::UNIT_Z,
-Vectors::UNIT_Z,
glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3),
-glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3),
glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3),
-glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3),
glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3),
-glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3),
glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3),
-glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3)
};
// returns true if the given point lies inside of the k-dop, specified by shapeInfo & shapePose.
// if the given point does lie within the k-dop, it also returns the amount of displacement necessary to push that point outward
// such that it lies on the surface of the kdop.
static bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose, const HFMJointShapeInfo& shapeInfo, glm::vec3& displacementOut) {
// transform point into local space of jointShape.
glm::vec3 localPoint = shapePose.inverse().xformPoint(point);
// Only works for 14-dop shape infos.
if (shapeInfo.dots.size() != DOP14_COUNT) {
return false;
}
glm::vec3 minDisplacement(FLT_MAX);
float minDisplacementLen = FLT_MAX;
glm::vec3 p = localPoint - shapeInfo.avgPoint;
float pLen = glm::length(p);
if (pLen > 0.0f) {
int slabCount = 0;
for (int i = 0; i < DOP14_COUNT; i++) {
float dot = glm::dot(p, DOP14_NORMALS[i]);
if (dot > 0.0f && dot < shapeInfo.dots[i]) {
slabCount++;
float distToPlane = pLen * (shapeInfo.dots[i] / dot);
float displacementLen = distToPlane - pLen;
// keep track of the smallest displacement
if (displacementLen < minDisplacementLen) {
minDisplacementLen = displacementLen;
minDisplacement = (p / pLen) * displacementLen;
}
}
}
if (slabCount == (DOP14_COUNT / 2) && minDisplacementLen != FLT_MAX) {
// we are within the k-dop so push the point along the minimum displacement found
displacementOut = shapePose.xformVectorFast(minDisplacement);
return true;
} else {
// point is outside of kdop
return false;
}
} else {
// point is directly on top of shapeInfo.avgPoint.
// push the point out along the x axis.
displacementOut = shapePose.xformVectorFast(shapeInfo.points[0]);
return true;
}
}
glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const HFMJointShapeInfo& hipsShapeInfo, const HFMJointShapeInfo& spineShapeInfo,
const HFMJointShapeInfo& spine1ShapeInfo, const HFMJointShapeInfo& spine2ShapeInfo) const {
glm::vec3 position = handPosition;

View file

@ -116,8 +116,9 @@ public:
void destroyAnimGraph();
void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
bool isPlayingOverrideAnimation() const { return _userAnimState.clipNodeEnum != UserAnimState::None; };
void restoreAnimation();
void overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
void triggerNetworkRole(const QString& role);
void restoreNetworkAnimation();
@ -333,7 +334,7 @@ protected:
RigRole _state { RigRole::Idle };
RigRole _desiredState { RigRole::Idle };
float _desiredStateAge { 0.0f };
struct NetworkAnimState {
enum ClipNodeEnum {
None = 0,

View file

@ -270,28 +270,19 @@ bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3&
getJointPosition(_rig.indexOfJoint("RightEye"), secondEyePosition)) {
return true;
}
// no eye joints; try to estimate based on head/neck joints
glm::vec3 neckPosition, headPosition;
if (getJointPosition(_rig.indexOfJoint("Neck"), neckPosition) &&
getJointPosition(_rig.indexOfJoint("Head"), headPosition)) {
const float EYE_PROPORTION = 0.6f;
glm::vec3 baseEyePosition = glm::mix(neckPosition, headPosition, EYE_PROPORTION);
int headJointIndex = _rig.indexOfJoint("Head");
glm::vec3 headPosition;
if (getJointPosition(headJointIndex, headPosition)) {
// get head joint rotation.
glm::quat headRotation;
getJointRotation(_rig.indexOfJoint("Head"), headRotation);
const float EYES_FORWARD = 0.25f;
const float EYE_SEPARATION = 0.1f;
float headHeight = glm::distance(neckPosition, headPosition);
firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD) * headHeight;
secondEyePosition = baseEyePosition + headRotation * glm::vec3(-EYE_SEPARATION, 0.0f, EYES_FORWARD) * headHeight;
return true;
} else if (getJointPosition(_rig.indexOfJoint("Head"), headPosition)) {
glm::vec3 baseEyePosition = headPosition;
glm::quat headRotation;
getJointRotation(_rig.indexOfJoint("Head"), headRotation);
const float EYES_FORWARD_HEAD_ONLY = 0.30f;
const float EYE_SEPARATION = 0.1f;
firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD_HEAD_ONLY);
secondEyePosition = baseEyePosition + headRotation * glm::vec3(-EYE_SEPARATION, 0.0f, EYES_FORWARD_HEAD_ONLY);
getJointRotation(headJointIndex, headRotation);
float heightRatio = _rig.getUnscaledEyeHeight() / DEFAULT_AVATAR_EYE_HEIGHT;
glm::vec3 ipdOffset = glm::vec3(DEFAULT_AVATAR_IPD / 2.0f, 0.0f, 0.0f);
firstEyePosition = headPosition + headRotation * heightRatio * (DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET + ipdOffset);
secondEyePosition = headPosition + headRotation * heightRatio * (DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET - ipdOffset);
return true;
}
return false;

View file

@ -144,7 +144,12 @@ void MaterialBaker::processMaterial() {
connect(textureBaker.data(), &TextureBaker::finished, this, &MaterialBaker::handleFinishedTextureBaker);
_textureBakers.insert(textureKey, textureBaker);
textureBaker->moveToThread(_getNextOvenWorkerThreadOperator ? _getNextOvenWorkerThreadOperator() : thread());
QMetaObject::invokeMethod(textureBaker.data(), "bake");
// By default, Qt will invoke this bake immediately if the TextureBaker is on the same worker thread as this MaterialBaker.
// We don't want that, because threads may be waiting for work while this thread is stuck processing a TextureBaker.
// On top of that, _textureBakers isn't fully populated.
// So, use Qt::QueuedConnection.
// TODO: Better thread utilization at the top level, not just the MaterialBaker level
QMetaObject::invokeMethod(textureBaker.data(), "bake", Qt::QueuedConnection);
}
_materialsNeedingRewrite.insert(textureKey, networkMaterial.second);
} else {

View file

@ -588,6 +588,8 @@ void LimitedNodeList::eraseAllNodes() {
foreach(const SharedNodePointer& killedNode, killedNodes) {
handleNodeKill(killedNode);
}
_delayedNodeAdds.clear();
}
void LimitedNodeList::reset() {
@ -755,7 +757,7 @@ void LimitedNodeList::delayNodeAdd(NewNodeInfo info) {
}
void LimitedNodeList::removeDelayedAdd(QUuid nodeUUID) {
auto it = std::find_if(_delayedNodeAdds.begin(), _delayedNodeAdds.end(), [&](auto info) {
auto it = std::find_if(_delayedNodeAdds.begin(), _delayedNodeAdds.end(), [&](const auto& info) {
return info.uuid == nodeUUID;
});
if (it != _delayedNodeAdds.end()) {
@ -764,7 +766,7 @@ void LimitedNodeList::removeDelayedAdd(QUuid nodeUUID) {
}
bool LimitedNodeList::isDelayedNode(QUuid nodeUUID) {
auto it = std::find_if(_delayedNodeAdds.begin(), _delayedNodeAdds.end(), [&](auto info) {
auto it = std::find_if(_delayedNodeAdds.begin(), _delayedNodeAdds.end(), [&](const auto& info) {
return info.uuid == nodeUUID;
});
return it != _delayedNodeAdds.end();

View file

@ -26,7 +26,7 @@ const quint16 ICE_SERVER_DEFAULT_PORT = 7337;
const int ICE_HEARBEAT_INTERVAL_MSECS = 2 * 1000;
const int MAX_ICE_CONNECTION_ATTEMPTS = 5;
const int UDP_PUNCH_PING_INTERVAL_MS = 25;
const int UDP_PUNCH_PING_INTERVAL_MS = 250;
class NetworkPeer : public QObject {
Q_OBJECT

View file

@ -752,11 +752,11 @@ void NodeList::pingPunchForInactiveNode(const SharedNodePointer& node) {
flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPing);
}
// every second we're trying to ping this node and we're not getting anywhere - debug that out
const int NUM_DEBUG_CONNECTION_ATTEMPTS = 1000 / (UDP_PUNCH_PING_INTERVAL_MS);
// every two seconds we're trying to ping this node and we're not getting anywhere - debug that out
const int NUM_DEBUG_CONNECTION_ATTEMPTS = 2000 / (UDP_PUNCH_PING_INTERVAL_MS);
if (node->getConnectionAttempts() > 0 && node->getConnectionAttempts() % NUM_DEBUG_CONNECTION_ATTEMPTS == 0) {
qCDebug(networking) << "No response to UDP hole punch pings for node" << node->getUUID() << "in last second.";
qCDebug(networking) << "No response to UDP hole punch pings for node" << node->getUUID() << "in last 2 s.";
}
auto nodeID = node->getUUID();

View file

@ -42,6 +42,7 @@ const float DEFAULT_AVATAR_HIPS_MASS = 40.0f;
const float DEFAULT_AVATAR_HEAD_MASS = 20.0f;
const float DEFAULT_AVATAR_LEFTHAND_MASS = 2.0f;
const float DEFAULT_AVATAR_RIGHTHAND_MASS = 2.0f;
const float DEFAULT_AVATAR_IPD = 0.064f;
// Used when avatar is missing joints... (avatar space)
const glm::quat DEFAULT_AVATAR_MIDDLE_EYE_ROT { Quaternions::Y_180 };
@ -102,6 +103,7 @@ static const float MAX_AVATAR_HEIGHT = 1000.0f * DEFAULT_AVATAR_HEIGHT; // meter
static const float MIN_AVATAR_HEIGHT = 0.005f * DEFAULT_AVATAR_HEIGHT; // meters
static const float MIN_AVATAR_RADIUS = 0.5f * MIN_AVATAR_HEIGHT;
static const float AVATAR_WALK_SPEED_SCALAR = 1.0f;
static const float AVATAR_SPRINT_SPEED_SCALAR = 2.0f;
static const float AVATAR_DESKTOP_SPRINT_SPEED_SCALAR = 3.0f;
static const float AVATAR_HMD_SPRINT_SPEED_SCALAR = 2.0f;
#endif // hifi_AvatarConstants_h

View file

@ -3503,6 +3503,7 @@ function loaded() {
deleteJSONMaterialEditor();
}
}
lastEntityID = null;
resetProperties();
showGroupsForType("None");

View file

@ -1347,12 +1347,16 @@ SelectionDisplay = (function() {
};
that.updateLastMouseEvent = function(event) {
if (activeTool && lastMouseEvent !== null) {
if (activeTool && lastMouseEvent !== null) {
var change = lastMouseEvent.isShifted !== event.isShifted || lastMouseEvent.isMeta !== event.isMeta ||
lastMouseEvent.isControl !== event.isControl || lastMouseEvent.isAlt !== event.isAlt;
lastMouseEvent.isShifted = event.isShifted;
lastMouseEvent.isMeta = event.isMeta;
lastMouseEvent.isControl = event.isControl;
lastMouseEvent.isAlt = event.isAlt;
activeTool.onMove(lastMouseEvent);
lastMouseEvent.isAlt = event.isAlt;
if (change) {
activeTool.onMove(lastMouseEvent);
}
}
};

View file

@ -412,9 +412,13 @@ void DomainBaker::enumerateEntities() {
if (entity.contains(MATERIAL_URL_KEY)) {
addMaterialBaker(MATERIAL_URL_KEY, entity[MATERIAL_URL_KEY].toString(), true, *it);
}
// FIXME: Disabled for now because relative texture URLs are not supported for embedded materials in material entities
// We need to make texture URLs absolute in this particular case only, keeping in mind that FSTBaker also uses embedded materials
/*
if (entity.contains(MATERIAL_DATA_KEY)) {
addMaterialBaker(MATERIAL_DATA_KEY, entity[MATERIAL_DATA_KEY].toString(), false, *it);
}
*/
}
}