Merge pull request #5710 from sethalves/fix-stuck-avatars

Fix stuck avatars
This commit is contained in:
Howard Stearns 2015-09-03 16:26:07 -07:00
commit 41daea7d92
6 changed files with 64 additions and 17 deletions

View file

@ -295,7 +295,8 @@ void AvatarMixer::broadcastAvatarData() {
avatarPacketList.startSegment();
numAvatarDataBytes += avatarPacketList.write(otherNode->getUUID().toRfc4122());
numAvatarDataBytes += avatarPacketList.write(otherAvatar.toByteArray(false));
numAvatarDataBytes +=
avatarPacketList.write(otherAvatar.toByteArray(false, randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO));
avatarPacketList.endSegment();
@ -364,6 +365,31 @@ void AvatarMixer::broadcastAvatarData() {
}
);
// We're done encoding this version of the otherAvatars. Update their "lastSent" joint-states so
// that we can notice differences, next time around.
nodeList->eachMatchingNode(
[&](const SharedNodePointer& otherNode)->bool {
if (!otherNode->getLinkedData()) {
return false;
}
if (otherNode->getType() != NodeType::Agent) {
return false;
}
if (!otherNode->getActiveSocket()) {
return false;
}
return true;
},
[&](const SharedNodePointer& otherNode) {
AvatarMixerClientData* otherNodeData = reinterpret_cast<AvatarMixerClientData*>(otherNode->getLinkedData());
MutexTryLocker lock(otherNodeData->getMutex());
if (!lock.isLocked()) {
return;
}
AvatarData& otherAvatar = otherNodeData->getAvatar();
otherAvatar.doneEncoding(false);
});
_lastFrameTimestamp = QDateTime::currentMSecsSinceEpoch();
}

View file

@ -123,18 +123,18 @@ MyAvatar::~MyAvatar() {
_lookAtTargetAvatar.reset();
}
QByteArray MyAvatar::toByteArray(bool cullSmallChanges) {
QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) {
CameraMode mode = Application::getInstance()->getCamera()->getMode();
if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) {
// fake the avatar position that is sent up to the AvatarMixer
glm::vec3 oldPosition = _position;
_position = getSkeletonPosition();
QByteArray array = AvatarData::toByteArray(cullSmallChanges);
QByteArray array = AvatarData::toByteArray(cullSmallChanges, sendAll);
// copy the correct position back
_position = oldPosition;
return array;
}
return AvatarData::toByteArray(cullSmallChanges);
return AvatarData::toByteArray(cullSmallChanges, sendAll);
}
void MyAvatar::reset() {

View file

@ -200,7 +200,7 @@ private:
glm::vec3 getWorldBodyPosition() const;
glm::quat getWorldBodyOrientation() const;
QByteArray toByteArray(bool cullSmallChanges);
QByteArray toByteArray(bool cullSmallChanges, bool sendAll);
void simulate(float deltaTime);
void updateFromTrackers(float deltaTime);
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPositio) override;

View file

@ -31,10 +31,6 @@
quint64 DEFAULT_FILTERED_LOG_EXPIRY = 2 * USECS_PER_SECOND;
// this controls how large a change in joint-rotation must be before the interface sends it to the avatar mixer
const float MIN_ROTATION_DOT = 0.9999999f;
using namespace std;
const glm::vec3 DEFAULT_LOCAL_AABOX_CORNER(-0.5f);
@ -145,7 +141,7 @@ void AvatarData::setHandPosition(const glm::vec3& handPosition) {
_handPosition = glm::inverse(getOrientation()) * (handPosition - _position);
}
QByteArray AvatarData::toByteArray(bool cullSmallChanges) {
QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
// TODO: DRY this up to a shared method
// that can pack any type given the number of bytes
// and return the number of bytes to push the pointer
@ -244,11 +240,12 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges) {
_lastSentJointData.resize(_jointData.size());
// foreach (const JointData& data, _jointData) {
for (int i=0; i < _jointData.size(); i++) {
const JointData& data = _jointData.at(i);
if (_lastSentJointData[i].rotation != data.rotation) {
if (!cullSmallChanges || fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= MIN_ROTATION_DOT) {
if (sendAll || _lastSentJointData[i].rotation != data.rotation) {
if (sendAll ||
!cullSmallChanges ||
fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= AVATAR_MIN_ROTATION_DOT) {
validity |= (1 << validityBit);
}
}
@ -267,7 +264,6 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges) {
const JointData& data = _jointData[ i ];
if (validity & (1 << validityBit)) {
destinationBuffer += packOrientationQuatToBytes(destinationBuffer, data.rotation);
_lastSentJointData[i].rotation = data.rotation;
}
if (++validityBit == BITS_IN_BYTE) {
validityBit = 0;
@ -278,6 +274,20 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges) {
return avatarDataByteArray.left(destinationBuffer - startPosition);
}
void AvatarData::doneEncoding(bool cullSmallChanges) {
// The server has finished sending this version of the joint-data to other nodes. Update _lastSentJointData.
_lastSentJointData.resize(_jointData.size());
for (int i = 0; i < _jointData.size(); i ++) {
const JointData& data = _jointData[ i ];
if (_lastSentJointData[i].rotation != data.rotation) {
if (!cullSmallChanges ||
fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= AVATAR_MIN_ROTATION_DOT) {
_lastSentJointData[i].rotation = data.rotation;
}
}
}
}
bool AvatarData::shouldLogError(const quint64& now) {
if (now > _errorLogExpiry) {
_errorLogExpiry = now + DEFAULT_FILTERED_LOG_EXPIRY;
@ -1083,7 +1093,11 @@ void AvatarData::setJointMappingsFromNetworkReply() {
void AvatarData::sendAvatarDataPacket() {
auto nodeList = DependencyManager::get<NodeList>();
QByteArray avatarByteArray = toByteArray(true);
// 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 sendFullUpdate = randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO;
QByteArray avatarByteArray = toByteArray(true, sendFullUpdate);
doneEncoding(true);
auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size());
avatarPacket->write(avatarByteArray);

View file

@ -111,6 +111,11 @@ const int AVATAR_BILLBOARD_PACKET_SEND_INTERVAL_MSECS = 5000;
// See also static AvatarData::defaultFullAvatarModelUrl().
const QString DEFAULT_FULL_AVATAR_MODEL_NAME = QString("Default");
// how often should we send a full report about joint rotations, even if they haven't changed?
const float AVATAR_SEND_FULL_UPDATE_RATIO = 0.02;
// this controls how large a change in joint-rotation must be before the interface sends it to the avatar mixer
const float AVATAR_MIN_ROTATION_DOT = 0.9999999f;
// Where one's own Avatar begins in the world (will be overwritten if avatar data file is found).
// This is the start location in the Sandbox (xyz: 6270, 211, 6000).
@ -171,7 +176,8 @@ public:
glm::vec3 getHandPosition() const;
void setHandPosition(const glm::vec3& handPosition);
virtual QByteArray toByteArray(bool cullSmallChanges);
virtual QByteArray toByteArray(bool cullSmallChanges, bool sendAll);
virtual void doneEncoding(bool cullSmallChanges);
/// \return true if an error should be logged
bool shouldLogError(const quint64& now);

View file

@ -599,7 +599,8 @@ void ScriptEngine::run() {
/ (1000 * 1000)) + 0.5);
const int SCRIPT_AUDIO_BUFFER_BYTES = SCRIPT_AUDIO_BUFFER_SAMPLES * sizeof(int16_t);
QByteArray avatarByteArray = _avatarData->toByteArray(true);
QByteArray avatarByteArray = _avatarData->toByteArray(true, randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO);
_avatarData->doneEncoding(true);
auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size());
avatarPacket->write(avatarByteArray);