mirror of
https://github.com/overte-org/overte.git
synced 2025-08-10 12:12:32 +02:00
Merge pull request #12225 from hyperlogic/bug-fix/sassy-avatar
Fix for avatars with crooked spine/head when exiting HMD mode
This commit is contained in:
commit
4697c5f92c
13 changed files with 243 additions and 79 deletions
|
@ -123,12 +123,12 @@ void ScriptableAvatar::update(float deltatime) {
|
||||||
AnimPose& absPose = absPoses[i];
|
AnimPose& absPose = absPoses[i];
|
||||||
if (data.rotation != absPose.rot()) {
|
if (data.rotation != absPose.rot()) {
|
||||||
data.rotation = absPose.rot();
|
data.rotation = absPose.rot();
|
||||||
data.rotationSet = true;
|
data.rotationIsDefaultPose = false;
|
||||||
}
|
}
|
||||||
AnimPose& relPose = poses[i];
|
AnimPose& relPose = poses[i];
|
||||||
if (data.translation != relPose.trans()) {
|
if (data.translation != relPose.trans()) {
|
||||||
data.translation = relPose.trans();
|
data.translation = relPose.trans();
|
||||||
data.translationSet = true;
|
data.translationIsDefaultPose = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2019,8 +2019,7 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
||||||
_smoothOrientationTimer = 0.0f;
|
_smoothOrientationTimer = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
getHead()->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime);
|
Head* head = getHead();
|
||||||
|
|
||||||
auto headPose = getControllerPoseInAvatarFrame(controller::Action::HEAD);
|
auto headPose = getControllerPoseInAvatarFrame(controller::Action::HEAD);
|
||||||
if (headPose.isValid()) {
|
if (headPose.isValid()) {
|
||||||
glm::quat localOrientation = headPose.rotation * Quaternions::Y_180;
|
glm::quat localOrientation = headPose.rotation * Quaternions::Y_180;
|
||||||
|
@ -2032,6 +2031,10 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
||||||
head->setBaseYaw(YAW(euler));
|
head->setBaseYaw(YAW(euler));
|
||||||
head->setBasePitch(PITCH(euler));
|
head->setBasePitch(PITCH(euler));
|
||||||
head->setBaseRoll(ROLL(euler));
|
head->setBaseRoll(ROLL(euler));
|
||||||
|
} else {
|
||||||
|
head->setBaseYaw(0.0f);
|
||||||
|
head->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime);
|
||||||
|
head->setBaseRoll(0.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -564,9 +564,9 @@ void ModelOverlay::animate() {
|
||||||
rotationMat * fbxJoints[index].postTransform);
|
rotationMat * fbxJoints[index].postTransform);
|
||||||
auto& jointData = jointsData[j];
|
auto& jointData = jointsData[j];
|
||||||
jointData.translation = extractTranslation(finalMat);
|
jointData.translation = extractTranslation(finalMat);
|
||||||
jointData.translationSet = true;
|
jointData.translationIsDefaultPose = false;
|
||||||
jointData.rotation = glmExtractRotation(finalMat);
|
jointData.rotation = glmExtractRotation(finalMat);
|
||||||
jointData.rotationSet = true;
|
jointData.rotationIsDefaultPose = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Set the data in the model
|
// Set the data in the model
|
||||||
|
|
|
@ -1705,16 +1705,16 @@ void Rig::copyJointsIntoJointData(QVector<JointData>& jointDataVec) const {
|
||||||
// rotations are in absolute rig frame.
|
// rotations are in absolute rig frame.
|
||||||
glm::quat defaultAbsRot = geometryToRigPose.rot() * _animSkeleton->getAbsoluteDefaultPose(i).rot();
|
glm::quat defaultAbsRot = geometryToRigPose.rot() * _animSkeleton->getAbsoluteDefaultPose(i).rot();
|
||||||
data.rotation = _internalPoseSet._absolutePoses[i].rot();
|
data.rotation = _internalPoseSet._absolutePoses[i].rot();
|
||||||
data.rotationSet = !isEqual(data.rotation, defaultAbsRot);
|
data.rotationIsDefaultPose = isEqual(data.rotation, defaultAbsRot);
|
||||||
|
|
||||||
// translations are in relative frame but scaled so that they are in meters,
|
// translations are in relative frame but scaled so that they are in meters,
|
||||||
// instead of geometry units.
|
// instead of geometry units.
|
||||||
glm::vec3 defaultRelTrans = _geometryOffset.scale() * _animSkeleton->getRelativeDefaultPose(i).trans();
|
glm::vec3 defaultRelTrans = _geometryOffset.scale() * _animSkeleton->getRelativeDefaultPose(i).trans();
|
||||||
data.translation = _geometryOffset.scale() * _internalPoseSet._relativePoses[i].trans();
|
data.translation = _geometryOffset.scale() * _internalPoseSet._relativePoses[i].trans();
|
||||||
data.translationSet = !isEqual(data.translation, defaultRelTrans);
|
data.translationIsDefaultPose = isEqual(data.translation, defaultRelTrans);
|
||||||
} else {
|
} else {
|
||||||
data.translationSet = false;
|
data.translationIsDefaultPose = true;
|
||||||
data.rotationSet = false;
|
data.rotationIsDefaultPose = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1739,11 +1739,11 @@ void Rig::copyJointsFromJointData(const QVector<JointData>& jointDataVec) {
|
||||||
const glm::quat rigToGeometryRot(glmExtractRotation(_rigToGeometryTransform));
|
const glm::quat rigToGeometryRot(glmExtractRotation(_rigToGeometryTransform));
|
||||||
for (int i = 0; i < numJoints; i++) {
|
for (int i = 0; i < numJoints; i++) {
|
||||||
const JointData& data = jointDataVec.at(i);
|
const JointData& data = jointDataVec.at(i);
|
||||||
if (data.rotationSet) {
|
if (data.rotationIsDefaultPose) {
|
||||||
|
rotations.push_back(absoluteDefaultPoses[i].rot());
|
||||||
|
} else {
|
||||||
// JointData rotations are in absolute rig-frame so we rotate them to absolute geometry-frame
|
// JointData rotations are in absolute rig-frame so we rotate them to absolute geometry-frame
|
||||||
rotations.push_back(rigToGeometryRot * data.rotation);
|
rotations.push_back(rigToGeometryRot * data.rotation);
|
||||||
} else {
|
|
||||||
rotations.push_back(absoluteDefaultPoses[i].rot());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1759,11 +1759,11 @@ void Rig::copyJointsFromJointData(const QVector<JointData>& jointDataVec) {
|
||||||
const JointData& data = jointDataVec.at(i);
|
const JointData& data = jointDataVec.at(i);
|
||||||
_internalPoseSet._relativePoses[i].scale() = Vectors::ONE;
|
_internalPoseSet._relativePoses[i].scale() = Vectors::ONE;
|
||||||
_internalPoseSet._relativePoses[i].rot() = rotations[i];
|
_internalPoseSet._relativePoses[i].rot() = rotations[i];
|
||||||
if (data.translationSet) {
|
if (data.translationIsDefaultPose) {
|
||||||
|
_internalPoseSet._relativePoses[i].trans() = relativeDefaultPoses[i].trans();
|
||||||
|
} else {
|
||||||
// JointData translations are in scaled relative-frame so we scale back to regular relative-frame
|
// JointData translations are in scaled relative-frame so we scale back to regular relative-frame
|
||||||
_internalPoseSet._relativePoses[i].trans() = _invGeometryOffset.scale() * data.translation;
|
_internalPoseSet._relativePoses[i].trans() = _invGeometryOffset.scale() * data.translation;
|
||||||
} else {
|
|
||||||
_internalPoseSet._relativePoses[i].trans() = relativeDefaultPoses[i].trans();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include <AudioHelpers.h>
|
#include <AudioHelpers.h>
|
||||||
#include <Profile.h>
|
#include <Profile.h>
|
||||||
#include <VariantMapToScriptValue.h>
|
#include <VariantMapToScriptValue.h>
|
||||||
|
#include <BitVectorHelpers.h>
|
||||||
|
|
||||||
#include "AvatarLogging.h"
|
#include "AvatarLogging.h"
|
||||||
|
|
||||||
|
@ -77,6 +78,16 @@ size_t AvatarDataPacket::maxJointDataSize(size_t numJoints) {
|
||||||
return totalSize;
|
return totalSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t AvatarDataPacket::maxJointDefaultPoseFlagsSize(size_t numJoints) {
|
||||||
|
const size_t bitVectorSize = calcBitVectorSize((int)numJoints);
|
||||||
|
size_t totalSize = sizeof(uint8_t); // numJoints
|
||||||
|
|
||||||
|
// one set of bits for rotation and one for translation
|
||||||
|
const size_t NUM_BIT_VECTORS_IN_DEFAULT_POSE_FLAGS_SECTION = 2;
|
||||||
|
totalSize += NUM_BIT_VECTORS_IN_DEFAULT_POSE_FLAGS_SECTION * bitVectorSize;
|
||||||
|
|
||||||
|
return totalSize;
|
||||||
|
}
|
||||||
|
|
||||||
AvatarData::AvatarData() :
|
AvatarData::AvatarData() :
|
||||||
SpatiallyNestable(NestableType::Avatar, QUuid()),
|
SpatiallyNestable(NestableType::Avatar, QUuid()),
|
||||||
|
@ -272,6 +283,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
|
|
||||||
bool hasFaceTrackerInfo = false;
|
bool hasFaceTrackerInfo = false;
|
||||||
bool hasJointData = false;
|
bool hasJointData = false;
|
||||||
|
bool hasJointDefaultPoseFlags = false;
|
||||||
|
|
||||||
if (sendPALMinimum) {
|
if (sendPALMinimum) {
|
||||||
hasAudioLoudness = true;
|
hasAudioLoudness = true;
|
||||||
|
@ -290,6 +302,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
|
|
||||||
hasFaceTrackerInfo = !dropFaceTracking && hasFaceTracker() && (sendAll || faceTrackerInfoChangedSince(lastSentTime));
|
hasFaceTrackerInfo = !dropFaceTracking && hasFaceTracker() && (sendAll || faceTrackerInfoChangedSince(lastSentTime));
|
||||||
hasJointData = sendAll || !sendMinimum;
|
hasJointData = sendAll || !sendMinimum;
|
||||||
|
hasJointDefaultPoseFlags = hasJointData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -314,7 +327,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
| (hasParentInfo ? AvatarDataPacket::PACKET_HAS_PARENT_INFO : 0)
|
| (hasParentInfo ? AvatarDataPacket::PACKET_HAS_PARENT_INFO : 0)
|
||||||
| (hasAvatarLocalPosition ? AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION : 0)
|
| (hasAvatarLocalPosition ? AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION : 0)
|
||||||
| (hasFaceTrackerInfo ? AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO : 0)
|
| (hasFaceTrackerInfo ? AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO : 0)
|
||||||
| (hasJointData ? AvatarDataPacket::PACKET_HAS_JOINT_DATA : 0);
|
| (hasJointData ? AvatarDataPacket::PACKET_HAS_JOINT_DATA : 0)
|
||||||
|
| (hasJointDefaultPoseFlags ? AvatarDataPacket::PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS : 0);
|
||||||
|
|
||||||
memcpy(destinationBuffer, &packetStateFlags, sizeof(packetStateFlags));
|
memcpy(destinationBuffer, &packetStateFlags, sizeof(packetStateFlags));
|
||||||
destinationBuffer += sizeof(packetStateFlags);
|
destinationBuffer += sizeof(packetStateFlags);
|
||||||
|
@ -541,14 +555,19 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
|
|
||||||
for (int i = 0; i < _jointData.size(); i++) {
|
for (int i = 0; i < _jointData.size(); i++) {
|
||||||
const JointData& data = _jointData[i];
|
const JointData& data = _jointData[i];
|
||||||
|
const JointData& last = lastSentJointData[i];
|
||||||
|
|
||||||
|
if (!data.rotationIsDefaultPose) {
|
||||||
|
if (sendAll || last.rotationIsDefaultPose || last.rotation != data.rotation) {
|
||||||
|
|
||||||
|
bool largeEnoughRotation = true;
|
||||||
|
if (cullSmallChanges) {
|
||||||
// The dot product for smaller rotations is a smaller number.
|
// The dot product for smaller rotations is a smaller number.
|
||||||
// So if the dot() is less than the value, then the rotation is a larger angle of rotation
|
// So if the dot() is less than the value, then the rotation is a larger angle of rotation
|
||||||
bool largeEnoughRotation = fabsf(glm::dot(data.rotation, lastSentJointData[i].rotation)) < minRotationDOT;
|
largeEnoughRotation = fabsf(glm::dot(last.rotation, data.rotation)) < minRotationDOT;
|
||||||
|
}
|
||||||
|
|
||||||
if (sendAll || lastSentJointData[i].rotation != data.rotation) {
|
|
||||||
if (sendAll || !cullSmallChanges || largeEnoughRotation) {
|
if (sendAll || !cullSmallChanges || largeEnoughRotation) {
|
||||||
if (data.rotationSet) {
|
|
||||||
validity |= (1 << validityBit);
|
validity |= (1 << validityBit);
|
||||||
#ifdef WANT_DEBUG
|
#ifdef WANT_DEBUG
|
||||||
rotationSentCount++;
|
rotationSentCount++;
|
||||||
|
@ -557,8 +576,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
|
|
||||||
if (sentJointDataOut) {
|
if (sentJointDataOut) {
|
||||||
localSentJointDataOut[i].rotation = data.rotation;
|
localSentJointDataOut[i].rotation = data.rotation;
|
||||||
|
localSentJointDataOut[i].rotationIsDefaultPose = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -588,11 +607,10 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
float maxTranslationDimension = 0.0;
|
float maxTranslationDimension = 0.0;
|
||||||
for (int i = 0; i < _jointData.size(); i++) {
|
for (int i = 0; i < _jointData.size(); i++) {
|
||||||
const JointData& data = _jointData[i];
|
const JointData& data = _jointData[i];
|
||||||
|
|
||||||
|
if (!data.translationIsDefaultPose) {
|
||||||
if (sendAll || lastSentJointData[i].translation != data.translation) {
|
if (sendAll || lastSentJointData[i].translation != data.translation) {
|
||||||
if (sendAll ||
|
if (sendAll || !cullSmallChanges || glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation) {
|
||||||
!cullSmallChanges ||
|
|
||||||
glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation) {
|
|
||||||
if (data.translationSet) {
|
|
||||||
validity |= (1 << validityBit);
|
validity |= (1 << validityBit);
|
||||||
#ifdef WANT_DEBUG
|
#ifdef WANT_DEBUG
|
||||||
translationSentCount++;
|
translationSentCount++;
|
||||||
|
@ -606,8 +624,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
|
|
||||||
if (sentJointDataOut) {
|
if (sentJointDataOut) {
|
||||||
localSentJointDataOut[i].translation = data.translation;
|
localSentJointDataOut[i].translation = data.translation;
|
||||||
|
localSentJointDataOut[i].translationIsDefaultPose = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -655,6 +673,30 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasJointDefaultPoseFlags) {
|
||||||
|
auto startSection = destinationBuffer;
|
||||||
|
QReadLocker readLock(&_jointDataLock);
|
||||||
|
|
||||||
|
// write numJoints
|
||||||
|
int numJoints = _jointData.size();
|
||||||
|
*destinationBuffer++ = (uint8_t)numJoints;
|
||||||
|
|
||||||
|
// write rotationIsDefaultPose bits
|
||||||
|
destinationBuffer += writeBitVector(destinationBuffer, numJoints, [&](int i) {
|
||||||
|
return _jointData[i].rotationIsDefaultPose;
|
||||||
|
});
|
||||||
|
|
||||||
|
// write translationIsDefaultPose bits
|
||||||
|
destinationBuffer += writeBitVector(destinationBuffer, numJoints, [&](int i) {
|
||||||
|
return _jointData[i].translationIsDefaultPose;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (outboundDataRateOut) {
|
||||||
|
size_t numBytes = destinationBuffer - startSection;
|
||||||
|
outboundDataRateOut->jointDefaultPoseFlagsRate.increment(numBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int avatarDataSize = destinationBuffer - startPosition;
|
int avatarDataSize = destinationBuffer - startPosition;
|
||||||
|
|
||||||
if (avatarDataSize > (int)byteArraySize) {
|
if (avatarDataSize > (int)byteArraySize) {
|
||||||
|
@ -664,6 +706,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
||||||
|
|
||||||
return avatarDataByteArray.left(avatarDataSize);
|
return avatarDataByteArray.left(avatarDataSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: This is never used in a "distanceAdjust" mode, so it's ok that it doesn't use a variable minimum rotation/translation
|
// NOTE: This is never used in a "distanceAdjust" mode, so it's ok that it doesn't use a variable minimum rotation/translation
|
||||||
void AvatarData::doneEncoding(bool cullSmallChanges) {
|
void AvatarData::doneEncoding(bool cullSmallChanges) {
|
||||||
// The server has finished sending this version of the joint-data to other nodes. Update _lastSentJointData.
|
// The server has finished sending this version of the joint-data to other nodes. Update _lastSentJointData.
|
||||||
|
@ -674,7 +717,7 @@ void AvatarData::doneEncoding(bool cullSmallChanges) {
|
||||||
if (_lastSentJointData[i].rotation != data.rotation) {
|
if (_lastSentJointData[i].rotation != data.rotation) {
|
||||||
if (!cullSmallChanges ||
|
if (!cullSmallChanges ||
|
||||||
fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= AVATAR_MIN_ROTATION_DOT) {
|
fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= AVATAR_MIN_ROTATION_DOT) {
|
||||||
if (data.rotationSet) {
|
if (!data.rotationIsDefaultPose) {
|
||||||
_lastSentJointData[i].rotation = data.rotation;
|
_lastSentJointData[i].rotation = data.rotation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -682,7 +725,7 @@ void AvatarData::doneEncoding(bool cullSmallChanges) {
|
||||||
if (_lastSentJointData[i].translation != data.translation) {
|
if (_lastSentJointData[i].translation != data.translation) {
|
||||||
if (!cullSmallChanges ||
|
if (!cullSmallChanges ||
|
||||||
glm::distance(data.translation, _lastSentJointData[i].translation) > AVATAR_MIN_TRANSLATION) {
|
glm::distance(data.translation, _lastSentJointData[i].translation) > AVATAR_MIN_TRANSLATION) {
|
||||||
if (data.translationSet) {
|
if (!data.translationIsDefaultPose) {
|
||||||
_lastSentJointData[i].translation = data.translation;
|
_lastSentJointData[i].translation = data.translation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -730,6 +773,7 @@ const unsigned char* unpackFauxJoint(const unsigned char* sourceBuffer, ThreadSa
|
||||||
|
|
||||||
// read data in packet starting at byte offset and return number of bytes parsed
|
// read data in packet starting at byte offset and return number of bytes parsed
|
||||||
int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
||||||
|
|
||||||
// lazily allocate memory for HeadData in case we're not an Avatar instance
|
// lazily allocate memory for HeadData in case we're not an Avatar instance
|
||||||
lazyInitHeadData();
|
lazyInitHeadData();
|
||||||
|
|
||||||
|
@ -757,6 +801,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
||||||
bool hasAvatarLocalPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION);
|
bool hasAvatarLocalPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION);
|
||||||
bool hasFaceTrackerInfo = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO);
|
bool hasFaceTrackerInfo = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO);
|
||||||
bool hasJointData = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_JOINT_DATA);
|
bool hasJointData = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_JOINT_DATA);
|
||||||
|
bool hasJointDefaultPoseFlags = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS);
|
||||||
|
|
||||||
quint64 now = usecTimestampNow();
|
quint64 now = usecTimestampNow();
|
||||||
|
|
||||||
|
@ -1055,7 +1100,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
||||||
if (validRotations[i]) {
|
if (validRotations[i]) {
|
||||||
sourceBuffer += unpackOrientationQuatFromSixBytes(sourceBuffer, data.rotation);
|
sourceBuffer += unpackOrientationQuatFromSixBytes(sourceBuffer, data.rotation);
|
||||||
_hasNewJointData = true;
|
_hasNewJointData = true;
|
||||||
data.rotationSet = true;
|
data.rotationIsDefaultPose = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1090,7 +1135,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
||||||
if (validTranslations[i]) {
|
if (validTranslations[i]) {
|
||||||
sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX);
|
sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX);
|
||||||
_hasNewJointData = true;
|
_hasNewJointData = true;
|
||||||
data.translationSet = true;
|
data.translationIsDefaultPose = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1110,6 +1155,32 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
|
||||||
_jointDataUpdateRate.increment();
|
_jointDataUpdateRate.increment();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasJointDefaultPoseFlags) {
|
||||||
|
auto startSection = sourceBuffer;
|
||||||
|
|
||||||
|
QWriteLocker writeLock(&_jointDataLock);
|
||||||
|
|
||||||
|
PACKET_READ_CHECK(JointDefaultPoseFlagsNumJoints, sizeof(uint8_t));
|
||||||
|
int numJoints = (int)*sourceBuffer++;
|
||||||
|
|
||||||
|
_jointData.resize(numJoints);
|
||||||
|
|
||||||
|
size_t bitVectorSize = calcBitVectorSize(numJoints);
|
||||||
|
PACKET_READ_CHECK(JointDefaultPoseFlagsRotationFlags, bitVectorSize);
|
||||||
|
sourceBuffer += readBitVector(sourceBuffer, numJoints, [&](int i, bool value) {
|
||||||
|
_jointData[i].rotationIsDefaultPose = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
PACKET_READ_CHECK(JointDefaultPoseFlagsTranslationFlags, bitVectorSize);
|
||||||
|
sourceBuffer += readBitVector(sourceBuffer, numJoints, [&](int i, bool value) {
|
||||||
|
_jointData[i].translationIsDefaultPose = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
int numBytesRead = sourceBuffer - startSection;
|
||||||
|
_jointDefaultPoseFlagsRate.increment(numBytesRead);
|
||||||
|
_jointDefaultPoseFlagsUpdateRate.increment();
|
||||||
|
}
|
||||||
|
|
||||||
int numBytesRead = sourceBuffer - startPosition;
|
int numBytesRead = sourceBuffer - startPosition;
|
||||||
_averageBytesReceived.updateAverage(numBytesRead);
|
_averageBytesReceived.updateAverage(numBytesRead);
|
||||||
|
|
||||||
|
@ -1146,6 +1217,8 @@ float AvatarData::getDataRate(const QString& rateName) const {
|
||||||
return _faceTrackerRate.rate() / BYTES_PER_KILOBIT;
|
return _faceTrackerRate.rate() / BYTES_PER_KILOBIT;
|
||||||
} else if (rateName == "jointData") {
|
} else if (rateName == "jointData") {
|
||||||
return _jointDataRate.rate() / BYTES_PER_KILOBIT;
|
return _jointDataRate.rate() / BYTES_PER_KILOBIT;
|
||||||
|
} else if (rateName == "jointDefaultPoseFlagsRate") {
|
||||||
|
return _jointDefaultPoseFlagsRate.rate() / BYTES_PER_KILOBIT;
|
||||||
} else if (rateName == "globalPositionOutbound") {
|
} else if (rateName == "globalPositionOutbound") {
|
||||||
return _outboundDataRate.globalPositionRate.rate() / BYTES_PER_KILOBIT;
|
return _outboundDataRate.globalPositionRate.rate() / BYTES_PER_KILOBIT;
|
||||||
} else if (rateName == "localPositionOutbound") {
|
} else if (rateName == "localPositionOutbound") {
|
||||||
|
@ -1170,6 +1243,8 @@ float AvatarData::getDataRate(const QString& rateName) const {
|
||||||
return _outboundDataRate.faceTrackerRate.rate() / BYTES_PER_KILOBIT;
|
return _outboundDataRate.faceTrackerRate.rate() / BYTES_PER_KILOBIT;
|
||||||
} else if (rateName == "jointDataOutbound") {
|
} else if (rateName == "jointDataOutbound") {
|
||||||
return _outboundDataRate.jointDataRate.rate() / BYTES_PER_KILOBIT;
|
return _outboundDataRate.jointDataRate.rate() / BYTES_PER_KILOBIT;
|
||||||
|
} else if (rateName == "jointDefaultPoseFlagsOutbound") {
|
||||||
|
return _outboundDataRate.jointDefaultPoseFlagsRate.rate() / BYTES_PER_KILOBIT;
|
||||||
}
|
}
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
@ -1236,9 +1311,9 @@ void AvatarData::setJointData(int index, const glm::quat& rotation, const glm::v
|
||||||
}
|
}
|
||||||
JointData& data = _jointData[index];
|
JointData& data = _jointData[index];
|
||||||
data.rotation = rotation;
|
data.rotation = rotation;
|
||||||
data.rotationSet = true;
|
data.rotationIsDefaultPose = false;
|
||||||
data.translation = translation;
|
data.translation = translation;
|
||||||
data.translationSet = true;
|
data.translationIsDefaultPose = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarData::clearJointData(int index) {
|
void AvatarData::clearJointData(int index) {
|
||||||
|
@ -1294,7 +1369,8 @@ void AvatarData::setJointData(const QString& name, const glm::quat& rotation, co
|
||||||
auto& jointData = _jointData[index];
|
auto& jointData = _jointData[index];
|
||||||
jointData.rotation = rotation;
|
jointData.rotation = rotation;
|
||||||
jointData.translation = translation;
|
jointData.translation = translation;
|
||||||
jointData.rotationSet = jointData.translationSet = true;
|
jointData.rotationIsDefaultPose = false;
|
||||||
|
jointData.translationIsDefaultPose = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1304,7 +1380,7 @@ void AvatarData::setJointRotation(const QString& name, const glm::quat& rotation
|
||||||
writeLockWithNamedJointIndex(name, [&](int index) {
|
writeLockWithNamedJointIndex(name, [&](int index) {
|
||||||
auto& data = _jointData[index];
|
auto& data = _jointData[index];
|
||||||
data.rotation = rotation;
|
data.rotation = rotation;
|
||||||
data.rotationSet = true;
|
data.rotationIsDefaultPose = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1314,7 +1390,7 @@ void AvatarData::setJointTranslation(const QString& name, const glm::vec3& trans
|
||||||
writeLockWithNamedJointIndex(name, [&](int index) {
|
writeLockWithNamedJointIndex(name, [&](int index) {
|
||||||
auto& data = _jointData[index];
|
auto& data = _jointData[index];
|
||||||
data.translation = translation;
|
data.translation = translation;
|
||||||
data.translationSet = true;
|
data.translationIsDefaultPose = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1328,7 +1404,7 @@ void AvatarData::setJointRotation(int index, const glm::quat& rotation) {
|
||||||
}
|
}
|
||||||
JointData& data = _jointData[index];
|
JointData& data = _jointData[index];
|
||||||
data.rotation = rotation;
|
data.rotation = rotation;
|
||||||
data.rotationSet = true;
|
data.rotationIsDefaultPose = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarData::setJointTranslation(int index, const glm::vec3& translation) {
|
void AvatarData::setJointTranslation(int index, const glm::vec3& translation) {
|
||||||
|
@ -1341,7 +1417,7 @@ void AvatarData::setJointTranslation(int index, const glm::vec3& translation) {
|
||||||
}
|
}
|
||||||
JointData& data = _jointData[index];
|
JointData& data = _jointData[index];
|
||||||
data.translation = translation;
|
data.translation = translation;
|
||||||
data.translationSet = true;
|
data.translationIsDefaultPose = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarData::clearJointData(const QString& name) {
|
void AvatarData::clearJointData(const QString& name) {
|
||||||
|
@ -1397,7 +1473,7 @@ void AvatarData::setJointRotations(const QVector<glm::quat>& jointRotations) {
|
||||||
for (int i = 0; i < size; ++i) {
|
for (int i = 0; i < size; ++i) {
|
||||||
auto& data = _jointData[i];
|
auto& data = _jointData[i];
|
||||||
data.rotation = jointRotations[i];
|
data.rotation = jointRotations[i];
|
||||||
data.rotationSet = true;
|
data.rotationIsDefaultPose = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1419,7 +1495,7 @@ void AvatarData::setJointTranslations(const QVector<glm::vec3>& jointTranslation
|
||||||
for (int i = 0; i < size; ++i) {
|
for (int i = 0; i < size; ++i) {
|
||||||
auto& data = _jointData[i];
|
auto& data = _jointData[i];
|
||||||
data.translation = jointTranslations[i];
|
data.translation = jointTranslations[i];
|
||||||
data.translationSet = true;
|
data.translationIsDefaultPose = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1996,9 +2072,9 @@ JointData jointDataFromJsonValue(const QJsonValue& json) {
|
||||||
if (json.isArray()) {
|
if (json.isArray()) {
|
||||||
QJsonArray array = json.toArray();
|
QJsonArray array = json.toArray();
|
||||||
result.rotation = quatFromJsonValue(array[0]);
|
result.rotation = quatFromJsonValue(array[0]);
|
||||||
result.rotationSet = true;
|
result.rotationIsDefaultPose = false;
|
||||||
result.translation = vec3FromJsonValue(array[1]);
|
result.translation = vec3FromJsonValue(array[1]);
|
||||||
result.translationSet = true;
|
result.translationIsDefaultPose = false;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,6 +125,7 @@ namespace AvatarDataPacket {
|
||||||
const HasFlags PACKET_HAS_AVATAR_LOCAL_POSITION = 1U << 9;
|
const HasFlags PACKET_HAS_AVATAR_LOCAL_POSITION = 1U << 9;
|
||||||
const HasFlags PACKET_HAS_FACE_TRACKER_INFO = 1U << 10;
|
const HasFlags PACKET_HAS_FACE_TRACKER_INFO = 1U << 10;
|
||||||
const HasFlags PACKET_HAS_JOINT_DATA = 1U << 11;
|
const HasFlags PACKET_HAS_JOINT_DATA = 1U << 11;
|
||||||
|
const HasFlags PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS = 1U << 12;
|
||||||
const size_t AVATAR_HAS_FLAGS_SIZE = 2;
|
const size_t AVATAR_HAS_FLAGS_SIZE = 2;
|
||||||
|
|
||||||
using SixByteQuat = uint8_t[6];
|
using SixByteQuat = uint8_t[6];
|
||||||
|
@ -256,6 +257,15 @@ namespace AvatarDataPacket {
|
||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
size_t maxJointDataSize(size_t numJoints);
|
size_t maxJointDataSize(size_t numJoints);
|
||||||
|
|
||||||
|
/*
|
||||||
|
struct JointDefaultPoseFlags {
|
||||||
|
uint8_t numJoints;
|
||||||
|
uint8_t rotationIsDefaultPoseBits[ceil(numJoints / 8)];
|
||||||
|
uint8_t translationIsDefaultPoseBits[ceil(numJoints / 8)];
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
size_t maxJointDefaultPoseFlagsSize(size_t numJoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
const float MAX_AUDIO_LOUDNESS = 1000.0f; // close enough for mouth animation
|
const float MAX_AUDIO_LOUDNESS = 1000.0f; // close enough for mouth animation
|
||||||
|
@ -321,6 +331,7 @@ public:
|
||||||
RateCounter<> parentInfoRate;
|
RateCounter<> parentInfoRate;
|
||||||
RateCounter<> faceTrackerRate;
|
RateCounter<> faceTrackerRate;
|
||||||
RateCounter<> jointDataRate;
|
RateCounter<> jointDataRate;
|
||||||
|
RateCounter<> jointDefaultPoseFlagsRate;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AvatarPriority {
|
class AvatarPriority {
|
||||||
|
@ -810,6 +821,7 @@ protected:
|
||||||
RateCounter<> _parentInfoRate;
|
RateCounter<> _parentInfoRate;
|
||||||
RateCounter<> _faceTrackerRate;
|
RateCounter<> _faceTrackerRate;
|
||||||
RateCounter<> _jointDataRate;
|
RateCounter<> _jointDataRate;
|
||||||
|
RateCounter<> _jointDefaultPoseFlagsRate;
|
||||||
|
|
||||||
// Some rate data for incoming data updates
|
// Some rate data for incoming data updates
|
||||||
RateCounter<> _parseBufferUpdateRate;
|
RateCounter<> _parseBufferUpdateRate;
|
||||||
|
@ -825,6 +837,7 @@ protected:
|
||||||
RateCounter<> _parentInfoUpdateRate;
|
RateCounter<> _parentInfoUpdateRate;
|
||||||
RateCounter<> _faceTrackerUpdateRate;
|
RateCounter<> _faceTrackerUpdateRate;
|
||||||
RateCounter<> _jointDataUpdateRate;
|
RateCounter<> _jointDataUpdateRate;
|
||||||
|
RateCounter<> _jointDefaultPoseFlagsUpdateRate;
|
||||||
|
|
||||||
// Some rate data for outgoing data
|
// Some rate data for outgoing data
|
||||||
AvatarDataRate _outboundDataRate;
|
AvatarDataRate _outboundDataRate;
|
||||||
|
|
|
@ -1050,7 +1050,7 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<JointData> jointsData;
|
QVector<EntityJointData> jointsData;
|
||||||
|
|
||||||
const QVector<FBXAnimationFrame>& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy
|
const QVector<FBXAnimationFrame>& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy
|
||||||
int frameCount = frames.size();
|
int frameCount = frames.size();
|
||||||
|
|
|
@ -452,7 +452,7 @@ void ModelEntityItem::resizeJointArrays(int newSize) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelEntityItem::setAnimationJointsData(const QVector<JointData>& jointsData) {
|
void ModelEntityItem::setAnimationJointsData(const QVector<EntityJointData>& jointsData) {
|
||||||
resizeJointArrays(jointsData.size());
|
resizeJointArrays(jointsData.size());
|
||||||
_jointDataLock.withWriteLock([&] {
|
_jointDataLock.withWriteLock([&] {
|
||||||
for (auto index = 0; index < jointsData.size(); ++index) {
|
for (auto index = 0; index < jointsData.size(); ++index) {
|
||||||
|
|
|
@ -124,7 +124,7 @@ public:
|
||||||
virtual void setJointTranslations(const QVector<glm::vec3>& translations);
|
virtual void setJointTranslations(const QVector<glm::vec3>& translations);
|
||||||
virtual void setJointTranslationsSet(const QVector<bool>& translationsSet);
|
virtual void setJointTranslationsSet(const QVector<bool>& translationsSet);
|
||||||
|
|
||||||
virtual void setAnimationJointsData(const QVector<JointData>& jointsData);
|
virtual void setAnimationJointsData(const QVector<EntityJointData>& jointsData);
|
||||||
|
|
||||||
QVector<glm::quat> getJointRotations() const;
|
QVector<glm::quat> getJointRotations() const;
|
||||||
QVector<bool> getJointRotationsSet() const;
|
QVector<bool> getJointRotationsSet() const;
|
||||||
|
@ -150,7 +150,7 @@ protected:
|
||||||
bool _jointTranslationsExplicitlySet{ false }; // were the joints set as a property or just side effect of animations
|
bool _jointTranslationsExplicitlySet{ false }; // were the joints set as a property or just side effect of animations
|
||||||
|
|
||||||
struct ModelJointData {
|
struct ModelJointData {
|
||||||
JointData joint;
|
EntityJointData joint;
|
||||||
bool rotationDirty { false };
|
bool rotationDirty { false };
|
||||||
bool translationDirty { false };
|
bool translationDirty { false };
|
||||||
};
|
};
|
||||||
|
|
|
@ -38,7 +38,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
||||||
case PacketType::AvatarData:
|
case PacketType::AvatarData:
|
||||||
case PacketType::BulkAvatarData:
|
case PacketType::BulkAvatarData:
|
||||||
case PacketType::KillAvatar:
|
case PacketType::KillAvatar:
|
||||||
return static_cast<PacketVersion>(AvatarMixerPacketVersion::UpdatedMannequinDefaultAvatar);
|
return static_cast<PacketVersion>(AvatarMixerPacketVersion::AvatarJointDefaultPoseFlags);
|
||||||
case PacketType::MessagesData:
|
case PacketType::MessagesData:
|
||||||
return static_cast<PacketVersion>(MessageDataVersion::TextOrBinaryData);
|
return static_cast<PacketVersion>(MessageDataVersion::TextOrBinaryData);
|
||||||
case PacketType::ICEServerHeartbeat:
|
case PacketType::ICEServerHeartbeat:
|
||||||
|
|
|
@ -247,7 +247,8 @@ enum class AvatarMixerPacketVersion : PacketVersion {
|
||||||
AvatarIdentitySequenceFront,
|
AvatarIdentitySequenceFront,
|
||||||
IsReplicatedInAvatarIdentity,
|
IsReplicatedInAvatarIdentity,
|
||||||
AvatarIdentityLookAtSnapping,
|
AvatarIdentityLookAtSnapping,
|
||||||
UpdatedMannequinDefaultAvatar
|
UpdatedMannequinDefaultAvatar,
|
||||||
|
AvatarJointDefaultPoseFlags
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class DomainConnectRequestVersion : PacketVersion {
|
enum class DomainConnectRequestVersion : PacketVersion {
|
||||||
|
|
58
libraries/shared/src/BitVectorHelpers.h
Normal file
58
libraries/shared/src/BitVectorHelpers.h
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
//
|
||||||
|
// BitVectorHelpers.h
|
||||||
|
// libraries/shared/src
|
||||||
|
//
|
||||||
|
// Created by Anthony Thibault on 1/19/18.
|
||||||
|
// Copyright 2018 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_BitVectorHelpers_h
|
||||||
|
#define hifi_BitVectorHelpers_h
|
||||||
|
|
||||||
|
size_t calcBitVectorSize(int numBits) {
|
||||||
|
return ((numBits - 1) >> 3) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// func should be of type bool func(int index)
|
||||||
|
template <typename F>
|
||||||
|
size_t writeBitVector(uint8_t* destinationBuffer, int numBits, const F& func) {
|
||||||
|
size_t totalBytes = ((numBits - 1) >> 3) + 1;
|
||||||
|
uint8_t* cursor = destinationBuffer;
|
||||||
|
uint8_t byte = 0;
|
||||||
|
uint8_t bit = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < numBits; i++) {
|
||||||
|
if (func(i)) {
|
||||||
|
byte |= (1 << bit);
|
||||||
|
}
|
||||||
|
if (++bit == BITS_IN_BYTE) {
|
||||||
|
*cursor++ = byte;
|
||||||
|
byte = 0;
|
||||||
|
bit = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return totalBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// func should be of type 'void func(int index, bool value)'
|
||||||
|
template <typename F>
|
||||||
|
size_t readBitVector(const uint8_t* sourceBuffer, int numBits, const F& func) {
|
||||||
|
size_t totalBytes = ((numBits - 1) >> 3) + 1;
|
||||||
|
const uint8_t* cursor = sourceBuffer;
|
||||||
|
uint8_t bit = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < numBits; i++) {
|
||||||
|
bool value = (bool)(*cursor & (1 << bit));
|
||||||
|
func(i, value);
|
||||||
|
if (++bit == BITS_IN_BYTE) {
|
||||||
|
cursor++;
|
||||||
|
bit = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return totalBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -5,14 +5,27 @@
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtc/quaternion.hpp>
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
// Used by the avatar mixer to describe a single joint
|
class EntityJointData {
|
||||||
// These are relative to their parent and translations are in meters
|
|
||||||
class JointData {
|
|
||||||
public:
|
public:
|
||||||
glm::quat rotation;
|
glm::quat rotation;
|
||||||
|
glm::vec3 translation;
|
||||||
bool rotationSet = false;
|
bool rotationSet = false;
|
||||||
glm::vec3 translation; // meters
|
|
||||||
bool translationSet = false;
|
bool translationSet = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Used by the avatar mixer to describe a single joint
|
||||||
|
// Translations relative to their parent and are in meters.
|
||||||
|
// Rotations are absolute (i.e. not relative to parent) and are in rig space.
|
||||||
|
class JointData {
|
||||||
|
public:
|
||||||
|
glm::quat rotation;
|
||||||
|
glm::vec3 translation;
|
||||||
|
|
||||||
|
// This indicates that the rotation or translation is the same as the defaultPose for the avatar.
|
||||||
|
// if true, it also means that the rotation or translation value in this structure is not valid and
|
||||||
|
// should be replaced by the avatar's actual default pose value.
|
||||||
|
bool rotationIsDefaultPose = true;
|
||||||
|
bool translationIsDefaultPose = true;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue