mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-04-14 08:16:03 +02:00
Fix for avatar glitches, head and legs folding into chest.
As a form of compression, when encoding avatar data we only send joints that have changed significantly from the previous packet. Also, if a joint has the same value as the avatar's default pose, we don't send the full value, instead we mark it with a single bit. This particular issue is caused by an interaction between the default value flag and the joint changed culling. When connecting to a domain for the first time, it's possible that a I-frame or 'full' packet will not be sent for several seconds. In this case, the AvatarMixer has no previous history for values that have not changed recently. This causes the AvatarMixer to broadcast incorrect values to other clients. Keep in mind that the AvatarMixer does not have access to the avatar FBX file, so it cannot make an educated guess for this value and uses zero values instead. (0, 0, 0) for translation and (0, 0, 0, 1) for rotation. When a translation value of zero is received for the lower legs it will cause the legs to fold into the avatar's chest. Also, we've uncovered a bug where sometimes values were not sent to the AvatarMixer even when the value was previously default. This would also exhibit the same issue where zero translation values would be sent. I've done three things to help mitigate this issue. 1) On first connect to a new AvatarMixer, the Avatar now sends a 'full' packet. 2) When a joint rotation or translation changes from the default value, we transmit it. No questions asked. 3) Once a SkeletonModel has finished loading we initialize the avatar's _jointData with the proper translations from the default pose. This will help the case where a client receives a non default value, but has no previous history. (cherry picked from commit 94efdf76a6454f316985f95d6a221adb24788d66)
This commit is contained in:
parent
40e34f578a
commit
c29bf51226
5 changed files with 44 additions and 8 deletions
|
@ -3398,6 +3398,13 @@ void MyAvatar::setModelScale(float scale) {
|
|||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setSessionUUID(const QUuid& sessionUUID) {
|
||||
Avatar::setSessionUUID(sessionUUID);
|
||||
|
||||
// transmit a "sendAll" packet to the AvatarMixer we just connected to.
|
||||
sendAvatarDataPacket(true);
|
||||
}
|
||||
|
||||
SpatialParentTree* MyAvatar::getParentTree() const {
|
||||
auto entityTreeRenderer = qApp->getEntities();
|
||||
EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr;
|
||||
|
|
|
@ -616,6 +616,7 @@ public slots:
|
|||
glm::quat getOrientationForAudio();
|
||||
|
||||
virtual void setModelScale(float scale) override;
|
||||
virtual void setSessionUUID(const QUuid& sessionUUID) override;
|
||||
|
||||
signals:
|
||||
void audioListenerModeChanged();
|
||||
|
|
|
@ -58,6 +58,13 @@ void SkeletonModel::initJointStates() {
|
|||
glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset);
|
||||
_rig.initJointStates(geometry, modelOffset);
|
||||
|
||||
{
|
||||
// initialize _jointData with proper values for default joints
|
||||
QVector<JointData> defaultJointData;
|
||||
_rig.copyJointsIntoJointData(defaultJointData);
|
||||
_owningAvatar->setRawJointData(defaultJointData);
|
||||
}
|
||||
|
||||
// Determine the default eye position for avatar scale = 1.0
|
||||
int headJointIndex = geometry.headJointIndex;
|
||||
if (0 > headJointIndex || headJointIndex >= _rig.getJointStateCount()) {
|
||||
|
|
|
@ -559,7 +559,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
const JointData& last = lastSentJointData[i];
|
||||
|
||||
if (!data.rotationIsDefaultPose) {
|
||||
if (sendAll || last.rotationIsDefaultPose || last.rotation != data.rotation) {
|
||||
bool mustSend = sendAll || last.rotationIsDefaultPose;
|
||||
if (mustSend || last.rotation != data.rotation) {
|
||||
|
||||
bool largeEnoughRotation = true;
|
||||
if (cullSmallChanges) {
|
||||
|
@ -568,7 +569,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
largeEnoughRotation = fabsf(glm::dot(last.rotation, data.rotation)) < minRotationDOT;
|
||||
}
|
||||
|
||||
if (sendAll || !cullSmallChanges || largeEnoughRotation) {
|
||||
if (mustSend || !cullSmallChanges || largeEnoughRotation) {
|
||||
validity |= (1 << validityBit);
|
||||
#ifdef WANT_DEBUG
|
||||
rotationSentCount++;
|
||||
|
@ -608,10 +609,12 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
float maxTranslationDimension = 0.0;
|
||||
for (int i = 0; i < _jointData.size(); i++) {
|
||||
const JointData& data = _jointData[i];
|
||||
const JointData& last = lastSentJointData[i];
|
||||
|
||||
if (!data.translationIsDefaultPose) {
|
||||
if (sendAll || lastSentJointData[i].translation != data.translation) {
|
||||
if (sendAll || !cullSmallChanges || glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation) {
|
||||
bool mustSend = sendAll || last.translationIsDefaultPose;
|
||||
if (mustSend || last.translation != data.translation) {
|
||||
if (mustSend || !cullSmallChanges || glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation) {
|
||||
validity |= (1 << validityBit);
|
||||
#ifdef WANT_DEBUG
|
||||
translationSentCount++;
|
||||
|
@ -669,6 +672,19 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
}
|
||||
|
||||
if (sentJointDataOut) {
|
||||
|
||||
// Mark default poses in lastSentJointData, so when they become non-default we send them.
|
||||
for (int i = 0; i < _jointData.size(); i++) {
|
||||
const JointData& data = _jointData[i];
|
||||
JointData& local = localSentJointDataOut[i];
|
||||
if (data.rotationIsDefaultPose) {
|
||||
local.rotationIsDefaultPose = true;
|
||||
}
|
||||
if (data.translationIsDefaultPose) {
|
||||
local.translationIsDefaultPose = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Push new sent joint data to sentJointDataOut
|
||||
sentJointDataOut->swap(localSentJointDataOut);
|
||||
}
|
||||
|
@ -1816,13 +1832,13 @@ void AvatarData::setJointMappingsFromNetworkReply() {
|
|||
networkReply->deleteLater();
|
||||
}
|
||||
|
||||
void AvatarData::sendAvatarDataPacket() {
|
||||
void AvatarData::sendAvatarDataPacket(bool sendAll) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
// about 2% of the time, we send a full update (meaning, we transmit all the joint data), even if nothing has changed.
|
||||
// this is to guard against a joint moving once, the packet getting lost, and the joint never moving again.
|
||||
|
||||
bool cullSmallData = (randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO);
|
||||
bool cullSmallData = !sendAll && (randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO);
|
||||
auto dataDetail = cullSmallData ? SendAllData : CullSmallData;
|
||||
QByteArray avatarByteArray = toByteArrayStateful(dataDetail);
|
||||
|
||||
|
|
|
@ -256,6 +256,11 @@ namespace AvatarDataPacket {
|
|||
SixByteQuat rotation[numValidRotations]; // encodeded and compressed by packOrientationQuatToSixBytes()
|
||||
uint8_t translationValidityBits[ceil(numJoints / 8)]; // one bit per joint, if true then a compressed translation follows.
|
||||
SixByteTrans translation[numValidTranslations]; // encodeded and compressed by packFloatVec3ToSignedTwoByteFixed()
|
||||
|
||||
SixByteQuat leftHandControllerRotation;
|
||||
SixByteTrans leftHandControllerTranslation;
|
||||
SixByteQuat rightHandControllerRotation;
|
||||
SixByteTrans rightHandControllerTranslation;
|
||||
};
|
||||
*/
|
||||
size_t maxJointDataSize(size_t numJoints);
|
||||
|
@ -707,11 +712,11 @@ signals:
|
|||
void sessionUUIDChanged();
|
||||
|
||||
public slots:
|
||||
void sendAvatarDataPacket();
|
||||
void sendAvatarDataPacket(bool sendAll = false);
|
||||
void sendIdentityPacket();
|
||||
|
||||
void setJointMappingsFromNetworkReply();
|
||||
void setSessionUUID(const QUuid& sessionUUID) {
|
||||
virtual void setSessionUUID(const QUuid& sessionUUID) {
|
||||
if (sessionUUID != getID()) {
|
||||
if (sessionUUID == QUuid()) {
|
||||
setID(AVATAR_SELF_ID);
|
||||
|
|
Loading…
Reference in a new issue