mirror of
https://github.com/AleziaKurdis/overte.git
synced 2025-04-08 06:32:35 +02:00
Merge pull request #15438 from luiscuenca/skeletonTrait
Skeleton Trait Implementation
This commit is contained in:
commit
4761a69a4d
13 changed files with 278 additions and 7 deletions
|
@ -179,7 +179,6 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message,
|
|||
if (packetTraitVersion > _lastReceivedTraitVersions[traitType]) {
|
||||
_avatar->processTrait(traitType, message.read(traitSize));
|
||||
_lastReceivedTraitVersions[traitType] = packetTraitVersion;
|
||||
|
||||
if (traitType == AvatarTraits::SkeletonModelURL) {
|
||||
// special handling for skeleton model URL, since we need to make sure it is in the whitelist
|
||||
checkSkeletonURLAgainstWhitelist(slaveSharedData, sendingNode, packetTraitVersion);
|
||||
|
|
|
@ -625,6 +625,8 @@ Menu::Menu() {
|
|||
avatar.get(), SLOT(setEnableDebugDrawAnimPose(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawPosition, 0, false,
|
||||
avatar.get(), SLOT(setEnableDebugDrawPosition(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawOtherSkeletons, 0, false,
|
||||
avatarManager.data(), SLOT(setEnableDebugDrawOtherSkeletons(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::MeshVisible, 0, true,
|
||||
avatar.get(), SLOT(setEnableMeshVisible(bool)));
|
||||
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
|
||||
|
|
|
@ -33,6 +33,7 @@ namespace MenuOption {
|
|||
const QString AnimDebugDrawBaseOfSupport = "Debug Draw Base of Support";
|
||||
const QString AnimDebugDrawDefaultPose = "Debug Draw Default Pose";
|
||||
const QString AnimDebugDrawPosition= "Debug Draw Position";
|
||||
const QString AnimDebugDrawOtherSkeletons = "Debug Draw Other Skeletons";
|
||||
const QString AskToResetSettings = "Ask To Reset Settings on Start";
|
||||
const QString AssetMigration = "ATP Asset Migration";
|
||||
const QString AssetServer = "Asset Browser";
|
||||
|
|
|
@ -120,6 +120,8 @@ void AvatarManager::init() {
|
|||
_myAvatar->addToScene(_myAvatar, scene, transaction);
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
|
||||
setEnableDebugDrawOtherSkeletons(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawOtherSkeletons));
|
||||
}
|
||||
|
||||
void AvatarManager::setSpace(workload::SpacePointer& space ) {
|
||||
|
@ -334,9 +336,14 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
if (avatar->getSkeletonModel()->isLoaded() && avatar->getWorkloadRegion() == workload::Region::R1) {
|
||||
_myAvatar->addAvatarHandsToFlow(avatar);
|
||||
}
|
||||
if (_drawOtherAvatarSkeletons) {
|
||||
avatar->debugJointData();
|
||||
}
|
||||
avatar->setEnableMeshVisible(!_drawOtherAvatarSkeletons);
|
||||
avatar->updateRenderItem(renderTransaction);
|
||||
avatar->updateSpaceProxy(workloadTransaction);
|
||||
avatar->setLastRenderUpdateTime(startTime);
|
||||
|
||||
} else {
|
||||
// we've spent our time budget for this priority bucket
|
||||
// let's deal with the reminding avatars if this pass and BREAK from the for loop
|
||||
|
|
|
@ -262,6 +262,15 @@ public slots:
|
|||
*/
|
||||
void updateAvatarRenderStatus(bool shouldRenderAvatars);
|
||||
|
||||
/**jsdoc
|
||||
* Displays other avatars skeletons debug graphics.
|
||||
* @function AvatarManager.setEnableDebugDrawOtherSkeletons
|
||||
* @param {boolean} enabled - <code>true</code> to show the debug graphics, <code>false</code> to hide.
|
||||
*/
|
||||
void setEnableDebugDrawOtherSkeletons(bool isEnabled) {
|
||||
_drawOtherAvatarSkeletons = isEnabled;
|
||||
}
|
||||
|
||||
protected:
|
||||
AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) override;
|
||||
|
||||
|
@ -299,6 +308,7 @@ private:
|
|||
workload::SpacePointer _space;
|
||||
|
||||
AvatarTransit::TransitConfig _transitConfig;
|
||||
bool _drawOtherAvatarSkeletons { false };
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarManager_h
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "Application.h"
|
||||
#include "AvatarMotionState.h"
|
||||
#include "DetailedMotionState.h"
|
||||
#include "DebugDraw.h"
|
||||
|
||||
const float DISPLAYNAME_FADE_TIME = 0.5f;
|
||||
const float DISPLAYNAME_FADE_FACTOR = pow(0.01f, 1.0f / DISPLAYNAME_FADE_TIME);
|
||||
|
@ -358,6 +359,58 @@ void OtherAvatar::simulate(float deltaTime, bool inView) {
|
|||
}
|
||||
}
|
||||
|
||||
void OtherAvatar::debugJointData() const {
|
||||
// Get a copy of the joint data
|
||||
auto jointData = getJointData();
|
||||
auto skeletonData = getSkeletonData();
|
||||
if ((int)skeletonData.size() == jointData.size() && jointData.size() != 0) {
|
||||
const vec4 RED(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
const vec4 GREEN(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
const vec4 BLUE(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
const vec4 LIGHT_RED(1.0f, 0.5f, 0.5f, 1.0f);
|
||||
const vec4 LIGHT_GREEN(0.5f, 1.0f, 0.5f, 1.0f);
|
||||
const vec4 LIGHT_BLUE(0.5f, 0.5f, 1.0f, 1.0f);
|
||||
const vec4 GREY(0.3f, 0.3f, 0.3f, 1.0f);
|
||||
const vec4 WHITE(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
const float AXIS_LENGTH = 0.1f;
|
||||
|
||||
AnimPoseVec absoluteJointPoses;
|
||||
AnimPose rigToAvatar = AnimPose(Quaternions::Y_180 * getWorldOrientation(), getWorldPosition());
|
||||
bool drawBones = false;
|
||||
for (int i = 0; i < jointData.size(); i++) {
|
||||
float jointScale = skeletonData[i].defaultScale * getTargetScale() * METERS_PER_CENTIMETER;
|
||||
auto absoluteRotation = jointData[i].rotationIsDefaultPose ? skeletonData[i].defaultRotation : jointData[i].rotation;
|
||||
auto localJointTranslation = jointScale * (jointData[i].translationIsDefaultPose ? skeletonData[i].defaultTranslation : jointData[i].translation);
|
||||
bool isHips = skeletonData[i].jointName == "Hips";
|
||||
if (isHips) {
|
||||
localJointTranslation = glm::vec3(0.0f);
|
||||
drawBones = true;
|
||||
}
|
||||
AnimPose absoluteParentPose;
|
||||
int parentIndex = skeletonData[i].parentIndex;
|
||||
if (parentIndex != -1 && parentIndex < (int)absoluteJointPoses.size()) {
|
||||
absoluteParentPose = absoluteJointPoses[parentIndex];
|
||||
}
|
||||
AnimPose absoluteJointPose = AnimPose(absoluteRotation, absoluteParentPose.trans() + absoluteParentPose.rot() * localJointTranslation);
|
||||
auto jointPose = rigToAvatar * absoluteJointPose;
|
||||
auto parentPose = rigToAvatar * absoluteParentPose;
|
||||
if (drawBones) {
|
||||
glm::vec3 xAxis = jointPose.rot() * Vectors::UNIT_X;
|
||||
glm::vec3 yAxis = jointPose.rot() * Vectors::UNIT_Y;
|
||||
glm::vec3 zAxis = jointPose.rot() * Vectors::UNIT_Z;
|
||||
|
||||
DebugDraw::getInstance().drawRay(jointPose.trans(), jointPose.trans() + AXIS_LENGTH * xAxis, jointData[i].rotationIsDefaultPose ? LIGHT_RED : RED);
|
||||
DebugDraw::getInstance().drawRay(jointPose.trans(), jointPose.trans() + AXIS_LENGTH * yAxis, jointData[i].rotationIsDefaultPose ? LIGHT_GREEN : GREEN);
|
||||
DebugDraw::getInstance().drawRay(jointPose.trans(), jointPose.trans() + AXIS_LENGTH * zAxis, jointData[i].rotationIsDefaultPose ? LIGHT_BLUE : BLUE);
|
||||
if (!isHips) {
|
||||
DebugDraw::getInstance().drawRay(jointPose.trans(), parentPose.trans(), jointData[i].translationIsDefaultPose ? WHITE : GREY);
|
||||
}
|
||||
}
|
||||
absoluteJointPoses.push_back(absoluteJointPose);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OtherAvatar::handleChangedAvatarEntityData() {
|
||||
PerformanceTimer perfTimer("attachments");
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ public:
|
|||
void setCollisionWithOtherAvatarsFlags() override;
|
||||
|
||||
void simulate(float deltaTime, bool inView) override;
|
||||
|
||||
void debugJointData() const;
|
||||
friend AvatarManager;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -1469,6 +1469,37 @@ QStringList Avatar::getJointNames() const {
|
|||
return result;
|
||||
}
|
||||
|
||||
std::vector<AvatarSkeletonTrait::UnpackedJointData> Avatar::getSkeletonDefaultData() {
|
||||
std::vector<AvatarSkeletonTrait::UnpackedJointData> defaultSkeletonData;
|
||||
if (_skeletonModel->isLoaded()) {
|
||||
auto& model = _skeletonModel->getHFMModel();
|
||||
auto& rig = _skeletonModel->getRig();
|
||||
float geometryToRigScale = extractScale(rig.getGeometryToRigTransform())[0];
|
||||
QStringList jointNames = getJointNames();
|
||||
int sizeCount = 0;
|
||||
for (int i = 0; i < jointNames.size(); i++) {
|
||||
AvatarSkeletonTrait::UnpackedJointData jointData;
|
||||
jointData.jointIndex = i;
|
||||
jointData.parentIndex = rig.getJointParentIndex(i);
|
||||
if (jointData.parentIndex == -1) {
|
||||
jointData.boneType = model.joints[i].isSkeletonJoint ? AvatarSkeletonTrait::BoneType::SkeletonRoot : AvatarSkeletonTrait::BoneType::NonSkeletonRoot;
|
||||
} else {
|
||||
jointData.boneType = model.joints[i].isSkeletonJoint ? AvatarSkeletonTrait::BoneType::SkeletonChild : AvatarSkeletonTrait::BoneType::NonSkeletonChild;
|
||||
}
|
||||
jointData.defaultRotation = rig.getAbsoluteDefaultPose(i).rot();
|
||||
jointData.defaultTranslation = getDefaultJointTranslation(i);
|
||||
float jointLocalScale = extractScale(model.joints[i].transform)[0];
|
||||
jointData.defaultScale = jointLocalScale / geometryToRigScale;
|
||||
jointData.jointName = jointNames[i];
|
||||
jointData.stringLength = jointNames[i].size();
|
||||
jointData.stringStart = sizeCount;
|
||||
sizeCount += jointNames[i].size();
|
||||
defaultSkeletonData.push_back(jointData);
|
||||
}
|
||||
}
|
||||
return defaultSkeletonData;
|
||||
}
|
||||
|
||||
glm::vec3 Avatar::getJointPosition(int index) const {
|
||||
glm::vec3 position;
|
||||
_skeletonModel->getJointPositionInWorldFrame(index, position);
|
||||
|
@ -1535,6 +1566,8 @@ void Avatar::rigReady() {
|
|||
buildSpine2SplineRatioCache();
|
||||
computeMultiSphereShapes();
|
||||
buildSpine2SplineRatioCache();
|
||||
setSkeletonData(getSkeletonDefaultData());
|
||||
sendSkeletonData();
|
||||
}
|
||||
|
||||
// rig has been reset.
|
||||
|
|
|
@ -199,6 +199,8 @@ public:
|
|||
virtual int getJointIndex(const QString& name) const override;
|
||||
virtual QStringList getJointNames() const override;
|
||||
|
||||
std::vector<AvatarSkeletonTrait::UnpackedJointData> getSkeletonDefaultData();
|
||||
|
||||
/**jsdoc
|
||||
* Gets the default rotation of a joint (in the current avatar) relative to its parent.
|
||||
* <p>For information on the joint hierarchy used, see
|
||||
|
|
|
@ -1633,6 +1633,13 @@ void AvatarData::setJointData(int index, const glm::quat& rotation, const glm::v
|
|||
data.translationIsDefaultPose = false;
|
||||
}
|
||||
|
||||
QVector<JointData> AvatarData::getJointData() const {
|
||||
QVector<JointData> jointData;
|
||||
QReadLocker readLock(&_jointDataLock);
|
||||
jointData = _jointData;
|
||||
return jointData;
|
||||
}
|
||||
|
||||
void AvatarData::clearJointData(int index) {
|
||||
if (index < 0 || index >= LOWEST_PSEUDO_JOINT_INDEX) {
|
||||
return;
|
||||
|
@ -1987,11 +1994,98 @@ QUrl AvatarData::getWireSafeSkeletonModelURL() const {
|
|||
return QUrl();
|
||||
}
|
||||
}
|
||||
QByteArray AvatarData::packSkeletonData() const {
|
||||
// Send an avatar trait packet with the skeleton data before the mesh is loaded
|
||||
int avatarDataSize = 0;
|
||||
QByteArray avatarDataByteArray;
|
||||
_avatarSkeletonDataLock.withReadLock([&] {
|
||||
// Add header
|
||||
AvatarSkeletonTrait::Header header;
|
||||
header.maxScaleDimension = 0.0f;
|
||||
header.maxTranslationDimension = 0.0f;
|
||||
header.numJoints = (uint8_t)_avatarSkeletonData.size();
|
||||
header.stringTableLength = 0;
|
||||
|
||||
for (size_t i = 0; i < _avatarSkeletonData.size(); i++) {
|
||||
header.stringTableLength += (uint16_t)_avatarSkeletonData[i].jointName.size();
|
||||
auto& translation = _avatarSkeletonData[i].defaultTranslation;
|
||||
header.maxTranslationDimension = std::max(header.maxTranslationDimension, std::max(std::max(translation.x, translation.y), translation.z));
|
||||
header.maxScaleDimension = std::max(header.maxScaleDimension, _avatarSkeletonData[i].defaultScale);
|
||||
}
|
||||
|
||||
const int byteArraySize = (int)sizeof(AvatarSkeletonTrait::Header) + (int)(header.numJoints * sizeof(AvatarSkeletonTrait::JointData)) + header.stringTableLength;
|
||||
avatarDataByteArray = QByteArray(byteArraySize, 0);
|
||||
unsigned char* destinationBuffer = reinterpret_cast<unsigned char*>(avatarDataByteArray.data());
|
||||
const unsigned char* const startPosition = destinationBuffer;
|
||||
|
||||
memcpy(destinationBuffer, &header, sizeof(header));
|
||||
destinationBuffer += sizeof(AvatarSkeletonTrait::Header);
|
||||
|
||||
QString stringTable = "";
|
||||
for (size_t i = 0; i < _avatarSkeletonData.size(); i++) {
|
||||
AvatarSkeletonTrait::JointData jdata;
|
||||
jdata.boneType = _avatarSkeletonData[i].boneType;
|
||||
jdata.parentIndex = _avatarSkeletonData[i].parentIndex;
|
||||
packFloatRatioToTwoByte((uint8_t*)(&jdata.defaultScale), _avatarSkeletonData[i].defaultScale / header.maxScaleDimension);
|
||||
packOrientationQuatToSixBytes(jdata.defaultRotation, _avatarSkeletonData[i].defaultRotation);
|
||||
packFloatVec3ToSignedTwoByteFixed(jdata.defaultTranslation, _avatarSkeletonData[i].defaultTranslation / header.maxTranslationDimension, TRANSLATION_COMPRESSION_RADIX);
|
||||
jdata.jointIndex = (uint16_t)i;
|
||||
jdata.stringStart = (uint16_t)_avatarSkeletonData[i].stringStart;
|
||||
jdata.stringLength = (uint8_t)_avatarSkeletonData[i].stringLength;
|
||||
stringTable += _avatarSkeletonData[i].jointName;
|
||||
memcpy(destinationBuffer, &jdata, sizeof(AvatarSkeletonTrait::JointData));
|
||||
destinationBuffer += sizeof(AvatarSkeletonTrait::JointData);
|
||||
}
|
||||
|
||||
memcpy(destinationBuffer, stringTable.toUtf8(), header.stringTableLength);
|
||||
destinationBuffer += header.stringTableLength;
|
||||
|
||||
avatarDataSize = destinationBuffer - startPosition;
|
||||
});
|
||||
return avatarDataByteArray.left(avatarDataSize);
|
||||
}
|
||||
|
||||
QByteArray AvatarData::packSkeletonModelURL() const {
|
||||
return getWireSafeSkeletonModelURL().toEncoded();
|
||||
}
|
||||
|
||||
void AvatarData::unpackSkeletonData(const QByteArray& data) {
|
||||
|
||||
const unsigned char* startPosition = reinterpret_cast<const unsigned char*>(data.data());
|
||||
const unsigned char* sourceBuffer = startPosition;
|
||||
|
||||
auto header = reinterpret_cast<const AvatarSkeletonTrait::Header*>(sourceBuffer);
|
||||
sourceBuffer += sizeof(const AvatarSkeletonTrait::Header);
|
||||
|
||||
std::vector<AvatarSkeletonTrait::UnpackedJointData> joints;
|
||||
for (uint8_t i = 0; i < header->numJoints; i++) {
|
||||
auto jointData = reinterpret_cast<const AvatarSkeletonTrait::JointData*>(sourceBuffer);
|
||||
sourceBuffer += sizeof(const AvatarSkeletonTrait::JointData);
|
||||
AvatarSkeletonTrait::UnpackedJointData uJointData;
|
||||
uJointData.boneType = (int)jointData->boneType;
|
||||
uJointData.jointIndex = (int)i;
|
||||
uJointData.stringLength = (int)jointData->stringLength;
|
||||
uJointData.stringStart = (int)jointData->stringStart;
|
||||
uJointData.parentIndex = ((uJointData.boneType == AvatarSkeletonTrait::BoneType::SkeletonRoot) ||
|
||||
(uJointData.boneType == AvatarSkeletonTrait::BoneType::NonSkeletonRoot)) ? -1 : (int)jointData->parentIndex;
|
||||
unpackOrientationQuatFromSixBytes(reinterpret_cast<const unsigned char*>(&jointData->defaultRotation), uJointData.defaultRotation);
|
||||
unpackFloatVec3FromSignedTwoByteFixed(reinterpret_cast<const unsigned char*>(&jointData->defaultTranslation), uJointData.defaultTranslation, TRANSLATION_COMPRESSION_RADIX);
|
||||
unpackFloatRatioFromTwoByte(reinterpret_cast<const unsigned char*>(&jointData->defaultScale), uJointData.defaultScale);
|
||||
uJointData.defaultTranslation *= header->maxTranslationDimension;
|
||||
uJointData.defaultScale *= header->maxScaleDimension;
|
||||
joints.push_back(uJointData);
|
||||
}
|
||||
QString table = QString::fromUtf8(reinterpret_cast<const char*>(sourceBuffer), (int)header->stringTableLength);
|
||||
for (size_t i = 0; i < joints.size(); i++) {
|
||||
QStringRef subString(&table, joints[i].stringStart, joints[i].stringLength);
|
||||
joints[i].jointName = subString.toString();
|
||||
}
|
||||
if (_clientTraitsHandler) {
|
||||
_clientTraitsHandler->markTraitUpdated(AvatarTraits::SkeletonData);
|
||||
}
|
||||
setSkeletonData(joints);
|
||||
}
|
||||
|
||||
void AvatarData::unpackSkeletonModelURL(const QByteArray& data) {
|
||||
auto skeletonModelURL = QUrl::fromEncoded(data);
|
||||
setSkeletonModelURL(skeletonModelURL);
|
||||
|
@ -2027,6 +2121,8 @@ QByteArray AvatarData::packTrait(AvatarTraits::TraitType traitType) const {
|
|||
// Call packer function
|
||||
if (traitType == AvatarTraits::SkeletonModelURL) {
|
||||
traitBinaryData = packSkeletonModelURL();
|
||||
} else if (traitType == AvatarTraits::SkeletonData) {
|
||||
traitBinaryData = packSkeletonData();
|
||||
}
|
||||
|
||||
return traitBinaryData;
|
||||
|
@ -2048,6 +2144,8 @@ QByteArray AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, Avat
|
|||
void AvatarData::processTrait(AvatarTraits::TraitType traitType, QByteArray traitBinaryData) {
|
||||
if (traitType == AvatarTraits::SkeletonModelURL) {
|
||||
unpackSkeletonModelURL(traitBinaryData);
|
||||
} else if (traitType == AvatarTraits::SkeletonData) {
|
||||
unpackSkeletonData(traitBinaryData);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2110,7 +2208,6 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
|||
}
|
||||
|
||||
_skeletonModelURL = expanded;
|
||||
|
||||
if (_clientTraitsHandler) {
|
||||
_clientTraitsHandler->markTraitUpdated(AvatarTraits::SkeletonModelURL);
|
||||
}
|
||||
|
@ -3008,6 +3105,26 @@ AABox AvatarData::computeBubbleBox(float bubbleScale) const {
|
|||
return box;
|
||||
}
|
||||
|
||||
void AvatarData::setSkeletonData(const std::vector<AvatarSkeletonTrait::UnpackedJointData>& skeletonData) {
|
||||
_avatarSkeletonDataLock.withWriteLock([&] {
|
||||
_avatarSkeletonData = skeletonData;
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<AvatarSkeletonTrait::UnpackedJointData> AvatarData::getSkeletonData() const {
|
||||
std::vector<AvatarSkeletonTrait::UnpackedJointData> skeletonData;
|
||||
_avatarSkeletonDataLock.withReadLock([&] {
|
||||
skeletonData = _avatarSkeletonData;
|
||||
});
|
||||
return skeletonData;
|
||||
}
|
||||
|
||||
void AvatarData::sendSkeletonData() const{
|
||||
if (_clientTraitsHandler) {
|
||||
_clientTraitsHandler->markTraitUpdated(AvatarTraits::SkeletonData);
|
||||
}
|
||||
}
|
||||
|
||||
AABox AvatarData::getDefaultBubbleBox() const {
|
||||
AABox bubbleBox(_defaultBubbleBox);
|
||||
bubbleBox.translate(_globalPosition);
|
||||
|
|
|
@ -145,6 +145,45 @@ const char AVATARDATA_FLAGS_MINIMUM = 0;
|
|||
|
||||
using SmallFloat = uint16_t; // a compressed float with less precision, user defined radix
|
||||
|
||||
namespace AvatarSkeletonTrait {
|
||||
enum BoneType {
|
||||
SkeletonRoot = 0,
|
||||
SkeletonChild,
|
||||
NonSkeletonRoot,
|
||||
NonSkeletonChild
|
||||
};
|
||||
|
||||
PACKED_BEGIN struct Header {
|
||||
float maxTranslationDimension;
|
||||
float maxScaleDimension;
|
||||
uint8_t numJoints;
|
||||
uint16_t stringTableLength;
|
||||
} PACKED_END;
|
||||
|
||||
PACKED_BEGIN struct JointData {
|
||||
uint16_t stringStart;
|
||||
uint8_t stringLength;
|
||||
uint8_t boneType;
|
||||
uint8_t defaultTranslation[6];
|
||||
uint8_t defaultRotation[6];
|
||||
uint16_t defaultScale;
|
||||
uint16_t jointIndex;
|
||||
uint16_t parentIndex;
|
||||
} PACKED_END;
|
||||
|
||||
struct UnpackedJointData {
|
||||
int stringStart;
|
||||
int stringLength;
|
||||
int boneType;
|
||||
glm::vec3 defaultTranslation;
|
||||
glm::quat defaultRotation;
|
||||
float defaultScale;
|
||||
int jointIndex;
|
||||
int parentIndex;
|
||||
QString jointName;
|
||||
};
|
||||
}
|
||||
|
||||
namespace AvatarDataPacket {
|
||||
|
||||
// NOTE: every time AvatarData is sent from mixer to client, it also includes the GUIID for the session
|
||||
|
@ -258,6 +297,7 @@ namespace AvatarDataPacket {
|
|||
PACKED_BEGIN struct AvatarLocalPosition {
|
||||
float localPosition[3]; // parent frame translation of the avatar
|
||||
} PACKED_END;
|
||||
|
||||
const size_t AVATAR_LOCAL_POSITION_SIZE = 12;
|
||||
static_assert(sizeof(AvatarLocalPosition) == AVATAR_LOCAL_POSITION_SIZE, "AvatarDataPacket::AvatarLocalPosition size doesn't match.");
|
||||
|
||||
|
@ -1420,6 +1460,10 @@ public:
|
|||
void setIsNewAvatar(bool isNewAvatar) { _isNewAvatar = isNewAvatar; }
|
||||
bool getIsNewAvatar() { return _isNewAvatar; }
|
||||
void setIsClientAvatar(bool isClientAvatar) { _isClientAvatar = isClientAvatar; }
|
||||
void setSkeletonData(const std::vector<AvatarSkeletonTrait::UnpackedJointData>& skeletonData);
|
||||
std::vector<AvatarSkeletonTrait::UnpackedJointData> getSkeletonData() const;
|
||||
void sendSkeletonData() const;
|
||||
QVector<JointData> getJointData() const;
|
||||
|
||||
signals:
|
||||
|
||||
|
@ -1598,12 +1642,13 @@ protected:
|
|||
bool hasParent() const { return !getParentID().isNull(); }
|
||||
bool hasFaceTracker() const { return _headData ? _headData->_isFaceTrackerConnected : false; }
|
||||
|
||||
QByteArray packSkeletonData() const;
|
||||
QByteArray packSkeletonModelURL() const;
|
||||
QByteArray packAvatarEntityTraitInstance(AvatarTraits::TraitInstanceID traitInstanceID);
|
||||
QByteArray packGrabTraitInstance(AvatarTraits::TraitInstanceID traitInstanceID);
|
||||
|
||||
void unpackSkeletonModelURL(const QByteArray& data);
|
||||
|
||||
void unpackSkeletonData(const QByteArray& data);
|
||||
|
||||
// isReplicated will be true on downstream Avatar Mixers and their clients, but false on the upstream "master"
|
||||
// Audio Mixer that the replicated avatar is connected to.
|
||||
|
@ -1720,6 +1765,9 @@ protected:
|
|||
AvatarGrabDataMap _avatarGrabData;
|
||||
bool _avatarGrabDataChanged { false }; // by network
|
||||
|
||||
mutable ReadWriteLockable _avatarSkeletonDataLock;
|
||||
std::vector<AvatarSkeletonTrait::UnpackedJointData> _avatarSkeletonData;
|
||||
|
||||
// used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers.
|
||||
ThreadSafeValueCache<glm::mat4> _sensorToWorldMatrixCache { glm::mat4() };
|
||||
ThreadSafeValueCache<glm::mat4> _controllerLeftHandMatrixCache { glm::mat4() };
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace AvatarTraits {
|
|||
|
||||
// Simple traits
|
||||
SkeletonModelURL = 0,
|
||||
|
||||
SkeletonData,
|
||||
// Instanced traits
|
||||
FirstInstancedTrait,
|
||||
AvatarEntity = FirstInstancedTrait,
|
||||
|
|
|
@ -107,8 +107,7 @@ int ClientTraitsHandler::sendChangedTraitsToMixer() {
|
|||
|
||||
if (initialSend || *simpleIt == Updated) {
|
||||
bytesWritten += AvatarTraits::packTrait(traitType, *traitsPacketList, *_owningAvatar);
|
||||
|
||||
|
||||
|
||||
if (traitType == AvatarTraits::SkeletonModelURL) {
|
||||
// keep track of our skeleton version in case we get an override back
|
||||
_currentSkeletonVersion = _currentTraitVersion;
|
||||
|
|
Loading…
Reference in a new issue