// // AvatarData.h // libraries/avatars/src // // Created by Stephen Birarda on 4/9/13. // Copyright 2013 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_AvatarData_h #define hifi_AvatarData_h #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "AABox.h" #include "AvatarTraits.h" #include "HeadData.h" #include "PathUtils.h" using AvatarSharedPointer = std::shared_ptr; using AvatarWeakPointer = std::weak_ptr; using AvatarHash = QHash; using AvatarEntityMap = QMap; using PackedAvatarEntityMap = QMap; // similar to AvatarEntityMap, but different internal format using AvatarEntityIDs = QSet; using AvatarGrabDataMap = QMap; using AvatarDataSequenceNumber = uint16_t; const int MAX_NUM_AVATAR_ENTITIES = 42; // avatar motion behaviors const quint32 AVATAR_MOTION_ACTION_MOTOR_ENABLED = 1U << 0; const quint32 AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED = 1U << 1; const quint32 AVATAR_MOTION_DEFAULTS = AVATAR_MOTION_ACTION_MOTOR_ENABLED | AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED; // these bits will be expanded as features are exposed const quint32 AVATAR_MOTION_SCRIPTABLE_BITS = AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED; // Bitset of state flags - we store the key state, hand state, Faceshift, eye tracking, and existence of // referential data in this bit set. The hand state is an octal, but is split into two sections to maintain // backward compatibility. The bits are ordered as such (0-7 left to right). // AA 6/1/18 added three more flags bits 8,9, and 10 for procedural audio, blink, and eye saccade enabled // // +-----+-----+-+-+-+--+--+--+--+-----+ // |K0,K1|H0,H1|F|E|R|H2|Au|Bl|Ey|xxxxx| // +-----+-----+-+-+-+--+--+--+--+-----+ // // Key state - K0,K1 is found in the 1st and 2nd bits // Hand state - H0,H1,H2 is found in the 3rd, 4th, and 8th bits // Face tracker - F is found in the 5th bit // Eye tracker - E is found in the 6th bit // Referential Data - R is found in the 7th bit // Procedural audio to mouth movement is enabled 8th bit // Procedural Blink is enabled 9th bit // Procedural Eyelid is enabled 10th bit // Procedural PROCEDURAL_BLINK_FACE_MOVEMENT is enabled 11th bit // Procedural Collide with other avatars is enabled 12th bit // Procedural Has Hero Priority is enabled 13th bit const int KEY_STATE_START_BIT = 0; // 1st and 2nd bits (UNUSED) const int HAND_STATE_START_BIT = 2; // 3rd and 4th bits (UNUSED) const int HAS_SCRIPTED_BLENDSHAPES = 4; // 5th bit const int HAS_PROCEDURAL_EYE_MOVEMENT = 5; // 6th bit const int HAS_REFERENTIAL = 6; // 7th bit const int HAND_STATE_FINGER_POINTING_BIT = 7; // 8th bit (UNUSED) const int AUDIO_ENABLED_FACE_MOVEMENT = 8; // 9th bit const int PROCEDURAL_EYE_FACE_MOVEMENT = 9; // 10th bit const int PROCEDURAL_BLINK_FACE_MOVEMENT = 10; // 11th bit const int COLLIDE_WITH_OTHER_AVATARS = 11; // 12th bit const int HAS_HERO_PRIORITY = 12; // 13th bit (be scared) /**jsdoc *

The pointing state of the hands is specified by the following values:

* * * * * * * * * * *
ValueDescription
0No hand is pointing.
1The left hand is pointing.
2The right hand is pointing.
4It is the index finger that is pointing.
*

The values for the hand states are added together to give the HandState value. For example, if the left * hand's finger is pointing, the value is 1 + 4 == 5. * @typedef {number} HandState */ const char HAND_STATE_NULL = 0; const char LEFT_HAND_POINTING_FLAG = 1; const char RIGHT_HAND_POINTING_FLAG = 2; const char IS_FINGER_POINTING_FLAG = 4; // AvatarData state flags - we store the details about the packet encoding in the first byte, // before the "header" structure 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 // this is 16bytes of data at 45hz that's 5.76kbps // it might be nice to use a dictionary to compress that // Packet State Flags - we store the details about the existence of other records in this bitset: // AvatarGlobalPosition, Avatar face tracker, eye tracking, and existence of using HasFlags = uint16_t; const HasFlags PACKET_HAS_AVATAR_GLOBAL_POSITION = 1U << 0; const HasFlags PACKET_HAS_AVATAR_BOUNDING_BOX = 1U << 1; const HasFlags PACKET_HAS_AVATAR_ORIENTATION = 1U << 2; const HasFlags PACKET_HAS_AVATAR_SCALE = 1U << 3; const HasFlags PACKET_HAS_LOOK_AT_POSITION = 1U << 4; const HasFlags PACKET_HAS_AUDIO_LOUDNESS = 1U << 5; const HasFlags PACKET_HAS_SENSOR_TO_WORLD_MATRIX = 1U << 6; const HasFlags PACKET_HAS_ADDITIONAL_FLAGS = 1U << 7; const HasFlags PACKET_HAS_PARENT_INFO = 1U << 8; const HasFlags PACKET_HAS_AVATAR_LOCAL_POSITION = 1U << 9; const HasFlags PACKET_HAS_HAND_CONTROLLERS = 1U << 10; const HasFlags PACKET_HAS_FACE_TRACKER_INFO = 1U << 11; const HasFlags PACKET_HAS_JOINT_DATA = 1U << 12; const HasFlags PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS = 1U << 13; const HasFlags PACKET_HAS_GRAB_JOINTS = 1U << 14; const size_t AVATAR_HAS_FLAGS_SIZE = 2; using SixByteQuat = uint8_t[6]; using SixByteTrans = uint8_t[6]; // NOTE: AvatarDataPackets start with a uint16_t sequence number that is not reflected in the Header structure. PACKED_BEGIN struct Header { HasFlags packetHasFlags; // state flags, indicated which additional records are included in the packet } PACKED_END; const size_t HEADER_SIZE = 2; static_assert(sizeof(Header) == HEADER_SIZE, "AvatarDataPacket::Header size doesn't match."); PACKED_BEGIN struct AvatarGlobalPosition { float globalPosition[3]; // avatar's position } PACKED_END; const size_t AVATAR_GLOBAL_POSITION_SIZE = 12; static_assert(sizeof(AvatarGlobalPosition) == AVATAR_GLOBAL_POSITION_SIZE, "AvatarDataPacket::AvatarGlobalPosition size doesn't match."); PACKED_BEGIN struct AvatarBoundingBox { float avatarDimensions[3]; // avatar's bounding box in world space units, but relative to the position. float boundOriginOffset[3]; // offset from the position of the avatar to the origin of the bounding box } PACKED_END; const size_t AVATAR_BOUNDING_BOX_SIZE = 24; static_assert(sizeof(AvatarBoundingBox) == AVATAR_BOUNDING_BOX_SIZE, "AvatarDataPacket::AvatarBoundingBox size doesn't match."); PACKED_BEGIN struct AvatarOrientation { SixByteQuat avatarOrientation; // encodeded and compressed by packOrientationQuatToSixBytes() } PACKED_END; const size_t AVATAR_ORIENTATION_SIZE = 6; static_assert(sizeof(AvatarOrientation) == AVATAR_ORIENTATION_SIZE, "AvatarDataPacket::AvatarOrientation size doesn't match."); PACKED_BEGIN struct AvatarScale { SmallFloat scale; // avatar's scale, compressed by packFloatRatioToTwoByte() } PACKED_END; const size_t AVATAR_SCALE_SIZE = 2; static_assert(sizeof(AvatarScale) == AVATAR_SCALE_SIZE, "AvatarDataPacket::AvatarScale size doesn't match."); PACKED_BEGIN struct LookAtPosition { float lookAtPosition[3]; // world space position that eyes are focusing on. // FIXME - unless the person has an eye tracker, this is simulated... // a) maybe we can just have the client calculate this // b) at distance this will be hard to discern and can likely be // descimated or dropped completely // // POTENTIAL SAVINGS - 12 bytes } PACKED_END; const size_t LOOK_AT_POSITION_SIZE = 12; static_assert(sizeof(LookAtPosition) == LOOK_AT_POSITION_SIZE, "AvatarDataPacket::LookAtPosition size doesn't match."); PACKED_BEGIN struct AudioLoudness { uint8_t audioLoudness; // current loudness of microphone compressed with packFloatGainToByte() } PACKED_END; const size_t AUDIO_LOUDNESS_SIZE = 1; static_assert(sizeof(AudioLoudness) == AUDIO_LOUDNESS_SIZE, "AvatarDataPacket::AudioLoudness size doesn't match."); PACKED_BEGIN struct SensorToWorldMatrix { // FIXME - these 20 bytes are only used by viewers if my avatar has "attachments" // we could save these bytes if no attachments are active. // // POTENTIAL SAVINGS - 20 bytes SixByteQuat sensorToWorldQuat; // 6 byte compressed quaternion part of sensor to world matrix uint16_t sensorToWorldScale; // uniform scale of sensor to world matrix float sensorToWorldTrans[3]; // fourth column of sensor to world matrix // FIXME - sensorToWorldTrans might be able to be better compressed if it was // relative to the avatar position. } PACKED_END; const size_t SENSOR_TO_WORLD_SIZE = 20; static_assert(sizeof(SensorToWorldMatrix) == SENSOR_TO_WORLD_SIZE, "AvatarDataPacket::SensorToWorldMatrix size doesn't match."); PACKED_BEGIN struct AdditionalFlags { uint16_t flags; // additional flags: hand state, key state, eye tracking } PACKED_END; const size_t ADDITIONAL_FLAGS_SIZE = 2; static_assert(sizeof(AdditionalFlags) == ADDITIONAL_FLAGS_SIZE, "AvatarDataPacket::AdditionalFlags size doesn't match."); // only present if HAS_REFERENTIAL flag is set in AvatarInfo.flags PACKED_BEGIN struct ParentInfo { uint8_t parentUUID[16]; // rfc 4122 encoded uint16_t parentJointIndex; } PACKED_END; const size_t PARENT_INFO_SIZE = 18; static_assert(sizeof(ParentInfo) == PARENT_INFO_SIZE, "AvatarDataPacket::ParentInfo size doesn't match."); // will only ever be included if the avatar has a parent but can change independent of changes to parent info // and so we keep it a separate record 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."); PACKED_BEGIN struct HandControllers { SixByteQuat leftHandRotation; SixByteTrans leftHandTranslation; SixByteQuat rightHandRotation; SixByteTrans rightHandTranslation; } PACKED_END; static const size_t HAND_CONTROLLERS_SIZE = 24; static_assert(sizeof(HandControllers) == HAND_CONTROLLERS_SIZE, "AvatarDataPacket::HandControllers size doesn't match."); const size_t MAX_CONSTANT_HEADER_SIZE = HEADER_SIZE + AVATAR_GLOBAL_POSITION_SIZE + AVATAR_BOUNDING_BOX_SIZE + AVATAR_ORIENTATION_SIZE + AVATAR_SCALE_SIZE + LOOK_AT_POSITION_SIZE + AUDIO_LOUDNESS_SIZE + SENSOR_TO_WORLD_SIZE + ADDITIONAL_FLAGS_SIZE + PARENT_INFO_SIZE + AVATAR_LOCAL_POSITION_SIZE + HAND_CONTROLLERS_SIZE; // variable length structure follows // only present if HAS_SCRIPTED_BLENDSHAPES flag is set in AvatarInfo.flags PACKED_BEGIN struct FaceTrackerInfo { float leftEyeBlink; float rightEyeBlink; float averageLoudness; float browAudioLift; uint8_t numBlendshapeCoefficients; // float blendshapeCoefficients[numBlendshapeCoefficients]; } PACKED_END; const size_t FACE_TRACKER_INFO_SIZE = 17; static_assert(sizeof(FaceTrackerInfo) == FACE_TRACKER_INFO_SIZE, "AvatarDataPacket::FaceTrackerInfo size doesn't match."); size_t maxFaceTrackerInfoSize(size_t numBlendshapeCoefficients); /* struct JointData { uint8_t numJoints; uint8_t rotationValidityBits[ceil(numJoints / 8)]; // one bit per joint, if true then a compressed rotation follows. 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. float maxTranslationDimension; // used to normalize fixed point translation values. SixByteTrans translation[numValidTranslations]; // normalized and compressed by packFloatVec3ToSignedTwoByteFixed() SixByteQuat leftHandControllerRotation; SixByteTrans leftHandControllerTranslation; SixByteQuat rightHandControllerRotation; SixByteTrans rightHandControllerTranslation; }; */ size_t maxJointDataSize(size_t numJoints); size_t minJointDataSize(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); PACKED_BEGIN struct FarGrabJoints { float leftFarGrabPosition[3]; // left controller far-grab joint position float leftFarGrabRotation[4]; // left controller far-grab joint rotation float rightFarGrabPosition[3]; // right controller far-grab joint position float rightFarGrabRotation[4]; // right controller far-grab joint rotation float mouseFarGrabPosition[3]; // mouse far-grab joint position float mouseFarGrabRotation[4]; // mouse far-grab joint rotation } PACKED_END; const size_t FAR_GRAB_JOINTS_SIZE = 84; static_assert(sizeof(FarGrabJoints) == FAR_GRAB_JOINTS_SIZE, "AvatarDataPacket::FarGrabJoints size doesn't match."); static const size_t MIN_BULK_PACKET_SIZE = NUM_BYTES_RFC4122_UUID + HEADER_SIZE; // AvatarIdentity packet: enum class IdentityFlag: quint32 {none, isReplicated = 0x1, lookAtSnapping = 0x2, verificationFailed = 0x4}; Q_DECLARE_FLAGS(IdentityFlags, IdentityFlag) struct SendStatus { HasFlags itemFlags { 0 }; bool sendUUID { false }; int rotationsSent { 0 }; // ie: index of next unsent joint int translationsSent { 0 }; operator bool() { return itemFlags == 0; } }; } const float MAX_AUDIO_LOUDNESS = 1000.0f; // close enough for mouth animation // 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.02f; // 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; const float AVATAR_MIN_TRANSLATION = 0.0001f; // quaternion dot products const float ROTATION_CHANGE_2D = 0.99984770f; // 2 degrees const float ROTATION_CHANGE_4D = 0.99939083f; // 4 degrees const float ROTATION_CHANGE_6D = 0.99862953f; // 6 degrees const float ROTATION_CHANGE_15D = 0.99144486f; // 15 degrees const float ROTATION_CHANGE_179D = 0.00872653f; // 179 degrees // rotation culling distance thresholds const float AVATAR_DISTANCE_LEVEL_1 = 12.5f; // meters const float AVATAR_DISTANCE_LEVEL_2 = 16.6f; // meters const float AVATAR_DISTANCE_LEVEL_3 = 25.0f; // meters const float AVATAR_DISTANCE_LEVEL_4 = 50.0f; // meters const float AVATAR_DISTANCE_LEVEL_5 = 200.0f; // meters // 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). const glm::vec3 START_LOCATION(6270, 211, 6000); // Avatar Transit Constants const float AVATAR_TRANSIT_MIN_TRIGGER_DISTANCE = 1.0f; const float AVATAR_TRANSIT_MAX_TRIGGER_DISTANCE = 30.0f; const int AVATAR_TRANSIT_FRAME_COUNT = 5; const float AVATAR_TRANSIT_FRAMES_PER_METER = 0.5f; const float AVATAR_TRANSIT_ABORT_DISTANCE = 0.1f; const bool AVATAR_TRANSIT_DISTANCE_BASED = false; const float AVATAR_TRANSIT_FRAMES_PER_SECOND = 30.0f; const float AVATAR_PRE_TRANSIT_FRAME_COUNT = 10.0f; const float AVATAR_POST_TRANSIT_FRAME_COUNT = 27.0f; enum KeyState { NO_KEY_DOWN = 0, INSERT_KEY_DOWN, DELETE_KEY_DOWN }; enum KillAvatarReason : uint8_t { NoReason = 0, AvatarDisconnected, AvatarIgnored, TheirAvatarEnteredYourBubble, YourAvatarEnteredTheirBubble }; Q_DECLARE_METATYPE(KillAvatarReason); class QDataStream; class AttachmentData; class Transform; using TransformPointer = std::shared_ptr; class AvatarDataRate { public: RateCounter<> globalPositionRate; RateCounter<> localPositionRate; RateCounter<> handControllersRate; RateCounter<> avatarBoundingBoxRate; RateCounter<> avatarOrientationRate; RateCounter<> avatarScaleRate; RateCounter<> lookAtPositionRate; RateCounter<> audioLoudnessRate; RateCounter<> sensorToWorldRate; RateCounter<> additionalFlagsRate; RateCounter<> parentInfoRate; RateCounter<> faceTrackerRate; RateCounter<> jointDataRate; RateCounter<> jointDefaultPoseFlagsRate; RateCounter<> farGrabJointRate; }; class AvatarPriority { public: AvatarPriority(AvatarSharedPointer a, float p) : avatar(a), priority(p) {} AvatarSharedPointer avatar; float priority; // NOTE: we invert the less-than operator to sort high priorities to front bool operator<(const AvatarPriority& other) const { return priority < other.priority; } }; class ClientTraitsHandler; class AvatarData : public QObject, public SpatiallyNestable { Q_OBJECT // IMPORTANT: The JSDoc for the following properties should be copied to MyAvatar.h and ScriptableAvatar.h. /* * @property {Vec3} position - The position of the avatar. * @property {number} scale=1.0 - The scale of the avatar. The value can be set to anything between 0.005 and * 1000.0. When the scale value is fetched, it may temporarily be further limited by the domain's settings. * @property {number} density - The density of the avatar in kg/m3. The density is used to work out its mass in * the application of physics. Read-only. * @property {Vec3} handPosition - A user-defined hand position, in world coordinates. The position moves with the avatar * but is otherwise not used or changed by Interface. * @property {number} bodyYaw - The left or right rotation about an axis running from the head to the feet of the avatar. * Yaw is sometimes called "heading". * @property {number} bodyPitch - The rotation about an axis running from shoulder to shoulder of the avatar. Pitch is * sometimes called "elevation". * @property {number} bodyRoll - The rotation about an axis running from the chest to the back of the avatar. Roll is * sometimes called "bank". * @property {Quat} orientation - The orientation of the avatar. * @property {Quat} headOrientation - The orientation of the avatar's head. * @property {number} headPitch - The rotation about an axis running from ear to ear of the avatar's head. Pitch is * sometimes called "elevation". * @property {number} headYaw - The rotation left or right about an axis running from the base to the crown of the avatar's * head. Yaw is sometimes called "heading". * @property {number} headRoll - The rotation about an axis running from the nose to the back of the avatar's head. Roll is * sometimes called "bank". * @property {Vec3} velocity - The current velocity of the avatar. * @property {Vec3} angularVelocity - The current angular velocity of the avatar. * @property {number} audioLoudness - The instantaneous loudness of the audio input that the avatar is injecting into the * domain. * @property {number} audioAverageLoudness - The rolling average loudness of the audio input that the avatar is injecting * into the domain. * @property {string} displayName - The avatar's display name. * @property {string} sessionDisplayName - displayName's sanitized and default version defined by the avatar * mixer rather than Interface clients. The result is unique among all avatars present in the domain at the time. * @property {boolean} lookAtSnappingEnabled=true - true if the avatar's eyes snap to look at another avatar's * eyes when the other avatar is in the line of sight and also has lookAtSnappingEnabled == true. * @property {string} skeletonModelURL - The avatar's FST file. * @property {AttachmentData[]} attachmentData - Information on the avatar's attachments. *

Deprecated: This property is deprecated and will be removed. Use avatar entities instead.

* @property {string[]} jointNames - The list of joints in the current avatar model. Read-only. * @property {Uuid} sessionUUID - Unique ID of the avatar in the domain. Read-only. * @property {Mat4} sensorToWorldMatrix - The scale, rotation, and translation transform from the user's real world to the * avatar's size, orientation, and position in the virtual world. Read-only. * @property {Mat4} controllerLeftHandMatrix - The rotation and translation of the left hand controller relative to the * avatar. Read-only. * @property {Mat4} controllerRightHandMatrix - The rotation and translation of the right hand controller relative to the * avatar. Read-only. * @property {number} sensorToWorldScale - The scale that transforms dimensions in the user's real world to the avatar's * size in the virtual world. Read-only. * @property {boolean} hasPriority - true if the avatar is in a "hero" zone, false if it isn't. * Read-only. * @property {boolean} hasScriptedBlendshapes=false - Set this to true before using the {@link MyAvatar.setBlendshape} method, * after you no longer want scripted control over the blendshapes set to back to false.
NOTE: this property will * automatically become true if the Controller system has valid facial blendshape actions. * @property {boolean} hasProceduralBlinkFaceMovement=true - By default avatars will blink automatically by animating facial * blendshapes. Set this property to false to disable this automatic blinking. This can be useful if you * wish to fully control the blink facial blendshapes via the {@link MyAvatar.setBlendshape} method. * @property {boolean} hasProceduralEyeFaceMovement=true - By default the avatar eye facial blendshapes will be adjusted * automatically as the eyes move. This will prevent the iris is never obscured by the upper or lower lids. Set this * property to false to disable this automatic movement. This can be useful if you wish to fully control * the eye blendshapes via the {@link MyAvatar.setBlendshape} method. * @property {boolean} hasAudioEnabledFaceMovement=true - By default the avatar mouth blendshapes will animate based on * the microphone audio. Set this property to false to disable that animaiton. This can be useful if you * wish to fully control the blink facial blendshapes via the {@link MyAvatar.setBlendshape} method. */ Q_PROPERTY(glm::vec3 position READ getWorldPosition WRITE setPositionViaScript) Q_PROPERTY(float scale READ getDomainLimitedScale WRITE setTargetScale) Q_PROPERTY(float density READ getDensity) Q_PROPERTY(glm::vec3 handPosition READ getHandPosition WRITE setHandPosition) Q_PROPERTY(float bodyYaw READ getBodyYaw WRITE setBodyYaw) Q_PROPERTY(float bodyPitch READ getBodyPitch WRITE setBodyPitch) Q_PROPERTY(float bodyRoll READ getBodyRoll WRITE setBodyRoll) Q_PROPERTY(glm::quat orientation READ getWorldOrientation WRITE setOrientationViaScript) Q_PROPERTY(glm::quat headOrientation READ getHeadOrientation WRITE setHeadOrientation) Q_PROPERTY(float headPitch READ getHeadPitch WRITE setHeadPitch) Q_PROPERTY(float headYaw READ getHeadYaw WRITE setHeadYaw) Q_PROPERTY(float headRoll READ getHeadRoll WRITE setHeadRoll) Q_PROPERTY(glm::vec3 velocity READ getWorldVelocity WRITE setWorldVelocity) Q_PROPERTY(glm::vec3 angularVelocity READ getWorldAngularVelocity WRITE setWorldAngularVelocity) Q_PROPERTY(float audioLoudness READ getAudioLoudness WRITE setAudioLoudness) Q_PROPERTY(float audioAverageLoudness READ getAudioAverageLoudness WRITE setAudioAverageLoudness) Q_PROPERTY(QString displayName READ getDisplayName WRITE setDisplayName NOTIFY displayNameChanged) // sessionDisplayName is sanitized, defaulted version displayName that is defined by the AvatarMixer rather than by Interface clients. // The result is unique among all avatars present at the time. Q_PROPERTY(QString sessionDisplayName READ getSessionDisplayName WRITE setSessionDisplayName NOTIFY sessionDisplayNameChanged) Q_PROPERTY(bool lookAtSnappingEnabled MEMBER _lookAtSnappingEnabled NOTIFY lookAtSnappingChanged) Q_PROPERTY(QString skeletonModelURL READ getSkeletonModelURLFromScript WRITE setSkeletonModelURLFromScript NOTIFY skeletonModelURLChanged) Q_PROPERTY(QVector attachmentData READ getAttachmentData WRITE setAttachmentData) Q_PROPERTY(QStringList jointNames READ getJointNames) Q_PROPERTY(QUuid sessionUUID READ getSessionUUID NOTIFY sessionUUIDChanged) Q_PROPERTY(glm::mat4 sensorToWorldMatrix READ getSensorToWorldMatrix) Q_PROPERTY(glm::mat4 controllerLeftHandMatrix READ getControllerLeftHandMatrix) Q_PROPERTY(glm::mat4 controllerRightHandMatrix READ getControllerRightHandMatrix) Q_PROPERTY(float sensorToWorldScale READ getSensorToWorldScale) Q_PROPERTY(bool hasPriority READ getHasPriority) Q_PROPERTY(bool hasScriptedBlendshapes READ getHasScriptedBlendshapes WRITE setHasScriptedBlendshapes) Q_PROPERTY(bool hasProceduralBlinkFaceMovement READ getHasProceduralBlinkFaceMovement WRITE setHasProceduralBlinkFaceMovement) Q_PROPERTY(bool hasProceduralEyeFaceMovement READ getHasProceduralEyeFaceMovement WRITE setHasProceduralEyeFaceMovement) Q_PROPERTY(bool hasAudioEnabledFaceMovement READ getHasAudioEnabledFaceMovement WRITE setHasAudioEnabledFaceMovement) public: virtual QString getName() const override { return QString("Avatar:") + _displayName; } static const QString FRAME_NAME; static void fromFrame(const QByteArray& frameData, AvatarData& avatar, bool useFrameSkeleton = true); static QByteArray toFrame(const AvatarData& avatar); AvatarData(); virtual ~AvatarData(); static const QUrl& defaultFullAvatarModelUrl(); const QUuid getSessionUUID() const { return getID(); } glm::vec3 getHandPosition() const; void setHandPosition(const glm::vec3& handPosition); typedef enum { NoData, PALMinimum, MinimumData, CullSmallData, IncludeSmallData, SendAllData } AvatarDataDetail; virtual QByteArray toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking = false); virtual QByteArray toByteArray(AvatarDataDetail dataDetail, quint64 lastSentTime, const QVector& lastSentJointData, AvatarDataPacket::SendStatus& sendStatus, bool dropFaceTracking, bool distanceAdjust, glm::vec3 viewerPosition, QVector* sentJointDataOut, int maxDataSize = 0, AvatarDataRate* outboundDataRateOut = nullptr) const; virtual void doneEncoding(bool cullSmallChanges); /// \return true if an error should be logged bool shouldLogError(const quint64& now); /// \param packet byte array of data /// \param offset number of bytes into packet where data starts /// \return number of bytes parsed virtual int parseDataFromBuffer(const QByteArray& buffer); virtual void setCollisionWithOtherAvatarsFlags() {}; // Body Rotation (degrees) float getBodyYaw() const; void setBodyYaw(float bodyYaw); float getBodyPitch() const; void setBodyPitch(float bodyPitch); float getBodyRoll() const; void setBodyRoll(float bodyRoll); virtual void setPositionViaScript(const glm::vec3& position); virtual void setOrientationViaScript(const glm::quat& orientation); virtual void updateAttitude(const glm::quat& orientation) {} glm::quat getHeadOrientation() const { lazyInitHeadData(); return _headData->getOrientation(); } void setHeadOrientation(const glm::quat& orientation) { if (_headData) { _headData->setOrientation(orientation); } } void setLookAtPosition(const glm::vec3& lookAtPosition) { if (_headData) { _headData->setLookAtPosition(lookAtPosition); } } void setBlendshapeCoefficients(const QVector& blendshapeCoefficients) { if (_headData) { _headData->setBlendshapeCoefficients(blendshapeCoefficients); } } // access to Head().set/getMousePitch (degrees) float getHeadPitch() const { return _headData->getBasePitch(); } void setHeadPitch(float value) { _headData->setBasePitch(value); } float getHeadYaw() const { return _headData->getBaseYaw(); } void setHeadYaw(float value) { _headData->setBaseYaw(value); } float getHeadRoll() const { return _headData->getBaseRoll(); } void setHeadRoll(float value) { _headData->setBaseRoll(value); } // access to Head().set/getAverageLoudness float getAudioLoudness() const { return _audioLoudness; } void setAudioLoudness(float audioLoudness) { if (audioLoudness != _audioLoudness) { _audioLoudnessChanged = usecTimestampNow(); } _audioLoudness = audioLoudness; } bool audioLoudnessChangedSince(quint64 time) const { return _audioLoudnessChanged >= time; } float getAudioAverageLoudness() const { return _audioAverageLoudness; } void setAudioAverageLoudness(float audioAverageLoudness) { _audioAverageLoudness = audioAverageLoudness; } // Scale virtual void setTargetScale(float targetScale); float getDomainLimitedScale() const; void setHasScriptedBlendshapes(bool hasScriptedBlendshapes); bool getHasScriptedBlendshapes() const; void setHasProceduralBlinkFaceMovement(bool hasProceduralBlinkFaceMovement); bool getHasProceduralBlinkFaceMovement() const; void setHasProceduralEyeFaceMovement(bool hasProceduralEyeFaceMovement); bool getHasProceduralEyeFaceMovement() const; void setHasAudioEnabledFaceMovement(bool hasAudioEnabledFaceMovement); bool getHasAudioEnabledFaceMovement() const; /**jsdoc * Gets the minimum scale allowed for this avatar in the current domain. * This value can change as the user changes avatars or when changing domains. * @function Avatar.getDomainMinScale * @returns {number} The minimum scale allowed for this avatar in the current domain. */ Q_INVOKABLE float getDomainMinScale() const; /**jsdoc * Gets the maximum scale allowed for this avatar in the current domain. * This value can change as the user changes avatars or when changing domains. * @function Avatar.getDomainMaxScale * @returns {number} The maximum scale allowed for this avatar in the current domain. */ Q_INVOKABLE float getDomainMaxScale() const; // Returns eye height of avatar in meters, ignoring avatar scale. // if _targetScale is 1 then this will be identical to getEyeHeight; virtual float getUnscaledEyeHeight() const { return DEFAULT_AVATAR_EYE_HEIGHT; } // returns true, if an acurate eye height estimage can be obtained by inspecting the avatar model skeleton and geometry, // not all subclasses of AvatarData have access to this data. virtual bool canMeasureEyeHeight() const { return false; } /**jsdoc * Gets the current eye height of the avatar. * This height is only an estimate and might be incorrect for avatars that are missing standard joints. * @function Avatar.getEyeHeight * @returns {number} The eye height of the avatar. */ Q_INVOKABLE virtual float getEyeHeight() const { return _targetScale * getUnscaledEyeHeight(); } /**jsdoc * Gets the current height of the avatar. * This height is only an estimate and might be incorrect for avatars that are missing standard joints. * @function Avatar.getHeight * @returns {number} The height of the avatar. */ Q_INVOKABLE virtual float getHeight() const; float getUnscaledHeight() const; void setDomainMinimumHeight(float domainMinimumHeight); void setDomainMaximumHeight(float domainMaximumHeight); /**jsdoc * Sets the pointing state of the hands to control where the laser emanates from. If the right index finger is pointing, the * laser emanates from the tip of that finger, otherwise it emanates from the palm. * @function Avatar.setHandState * @param {HandState} state - The pointing state of the hand. */ Q_INVOKABLE void setHandState(char s) { _handState = s; } /**jsdoc * Gets the pointing state of the hands to control where the laser emanates from. If the right index finger is pointing, the * laser emanates from the tip of that finger, otherwise it emanates from the palm. * @function Avatar.getHandState * @returns {HandState} The pointing state of the hand. */ Q_INVOKABLE char getHandState() const { return _handState; } const QVector& getRawJointData() const { return _jointData; } /**jsdoc * Sets joint translations and rotations from raw joint data. * @function Avatar.setRawJointData * @param {JointData[]} data - The raw joint data. * @deprecated This function is deprecated and will be removed. */ Q_INVOKABLE void setRawJointData(QVector data); /**jsdoc * Sets a specific joint's rotation and position relative to its parent, in model coordinates. *

Warning: These coordinates are not necessarily in meters.

*

Setting joint data completely overrides/replaces all motion from the default animation system including inverse * kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints, * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate * joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set * the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.

* @function Avatar.setJointData * @param {number} index - The index of the joint. * @param {Quat} rotation - The rotation of the joint relative to its parent. * @param {Vec3} translation - The translation of the joint relative to its parent, in model coordinates. * @example Set your avatar to it's default T-pose for a while.
* Avatar in T-pose * // Set all joint translations and rotations to defaults. * var i, length, rotation, translation; * for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) { * rotation = MyAvatar.getDefaultJointRotation(i); * translation = MyAvatar.getDefaultJointTranslation(i); * MyAvatar.setJointData(i, rotation, translation); * } * * // Restore your avatar's motion after 5s. * Script.setTimeout(function () { * MyAvatar.clearJointsData(); * }, 5000); * * // Note: If using from the Avatar API, replace all occurrences of "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual void setJointData(int index, const glm::quat& rotation, const glm::vec3& translation); /**jsdoc * Sets a specific joint's rotation relative to its parent. *

Setting joint data completely overrides/replaces all motion from the default animation system including inverse * kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints, * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate * joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set * the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.

* @function Avatar.setJointRotation * @param {number} index - The index of the joint. * @param {Quat} rotation - The rotation of the joint relative to its parent. */ Q_INVOKABLE virtual void setJointRotation(int index, const glm::quat& rotation); /**jsdoc * Sets a specific joint's translation relative to its parent, in model coordinates. *

Warning: These coordinates are not necessarily in meters.

*

Setting joint data completely overrides/replaces all motion from the default animation system including inverse * kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints, * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate * joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set * the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.

* @function Avatar.setJointTranslation * @param {number} index - The index of the joint. * @param {Vec3} translation - The translation of the joint relative to its parent, in model coordinates. */ Q_INVOKABLE virtual void setJointTranslation(int index, const glm::vec3& translation); /**jsdoc * Clears joint translations and rotations set by script for a specific joint. This restores all motion from the default * animation system including inverse kinematics for that joint. *

Note: This is slightly faster than the function variation that specifies the joint name.

* @function Avatar.clearJointData * @param {number} index - The index of the joint. */ Q_INVOKABLE virtual void clearJointData(int index); /**jsdoc * Checks that the data for a joint are valid. * @function Avatar.isJointDataValid * @param {number} index - The index of the joint. * @returns {boolean} true if the joint data are valid, false if not. */ Q_INVOKABLE bool isJointDataValid(int index) const; /**jsdoc * Gets the rotation of a joint relative to its parent. For information on the joint hierarchy used, see * Avatar Standards. * @function Avatar.getJointRotation * @param {number} index - The index of the joint. * @returns {Quat} The rotation of the joint relative to its parent. */ Q_INVOKABLE virtual glm::quat getJointRotation(int index) const; /**jsdoc * Gets the translation of a joint relative to its parent, in model coordinates. *

Warning: These coordinates are not necessarily in meters.

*

For information on the joint hierarchy used, see * Avatar Standards.

* @function Avatar.getJointTranslation * @param {number} index - The index of the joint. * @returns {Vec3} The translation of the joint relative to its parent, in model coordinates. */ Q_INVOKABLE virtual glm::vec3 getJointTranslation(int index) const; /**jsdoc * Sets a specific joint's rotation and position relative to its parent, in model coordinates. *

Warning: These coordinates are not necessarily in meters.

*

Setting joint data completely overrides/replaces all motion from the default animation system including inverse * kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints, * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate * joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set * the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.

* @function Avatar.setJointData * @param {string} name - The name of the joint. * @param {Quat} rotation - The rotation of the joint relative to its parent. * @param {Vec3} translation - The translation of the joint relative to its parent, in model coordinates. */ Q_INVOKABLE virtual void setJointData(const QString& name, const glm::quat& rotation, const glm::vec3& translation); /**jsdoc * Sets a specific joint's rotation relative to its parent. *

Setting joint data completely overrides/replaces all motion from the default animation system including inverse * kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints, * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate * joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set * the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.

* @function Avatar.setJointRotation * @param {string} name - The name of the joint. * @param {Quat} rotation - The rotation of the joint relative to its parent. * @example Set your avatar to its default T-pose then rotate its right arm.
* Avatar in T-pose with arm rotated * // Set all joint translations and rotations to defaults. * var i, length, rotation, translation; * for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) { * rotation = MyAvatar.getDefaultJointRotation(i); * translation = MyAvatar.getDefaultJointTranslation(i); * MyAvatar.setJointData(i, rotation, translation); * } * * // Rotate the right arm. * var newArmRotation = { x: 0.47, y: 0.22, z: -0.02, w: 0.87 }; * MyAvatar.setJointRotation("RightArm", newArmRotation); * * // Restore your avatar's motion after 5s. * Script.setTimeout(function () { * MyAvatar.clearJointsData(); * }, 5000); * * // Note: If using from the Avatar API, replace all occurrences of "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual void setJointRotation(const QString& name, const glm::quat& rotation); /**jsdoc * Sets a specific joint's translation relative to its parent, in model coordinates. *

Warning: These coordinates are not necessarily in meters.

*

Setting joint data completely overrides/replaces all motion from the default animation system including inverse * kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints, * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate * joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set * the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.

* @function Avatar.setJointTranslation * @param {string} name - The name of the joint. * @param {Vec3} translation - The translation of the joint relative to its parent, in model coordinates. * @example Stretch your avatar's neck. Depending on the avatar you are using, you will either see a gap between * the head and body or you will see the neck stretched.
* Avatar with neck stretched * // Stretch your avatar's neck. * MyAvatar.setJointTranslation("Neck", Vec3.multiply(2, MyAvatar.getJointTranslation("Neck"))); * * // Restore your avatar's neck after 5s. * Script.setTimeout(function () { * MyAvatar.clearJointData("Neck"); * }, 5000); * * // Note: If using from the Avatar API, replace all occurrences of "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual void setJointTranslation(const QString& name, const glm::vec3& translation); /**jsdoc * Clears joint translations and rotations set by script for a specific joint. This restores all motion from the default * animation system including inverse kinematics for that joint. *

Note: This is slightly slower than the function variation that specifies the joint index.

* @function Avatar.clearJointData * @param {string} name - The name of the joint. * @example Offset and restore the position of your avatar's head. * // Stretch your avatar's neck. * MyAvatar.setJointTranslation("Neck", Vec3.multiply(2, MyAvatar.getJointTranslation("Neck"))); * * // Restore your avatar's neck after 5s. * Script.setTimeout(function () { * MyAvatar.clearJointData("Neck"); * }, 5000); * * // Note: If using from the Avatar API, replace all occurrences of "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual void clearJointData(const QString& name); /**jsdoc * Checks if the data for a joint are valid. * @function Avatar.isJointDataValid * @param {string} name - The name of the joint. * @returns {boolean} true if the joint data are valid, false if not. */ Q_INVOKABLE virtual bool isJointDataValid(const QString& name) const; /**jsdoc * Gets the rotation of a joint relative to its parent. For information on the joint hierarchy used, see * Avatar Standards. * @function Avatar.getJointRotation * @param {string} name - The name of the joint. * @returns {Quat} The rotation of the joint relative to its parent. * @example Report the rotation of your avatar's hips joint. * print(JSON.stringify(MyAvatar.getJointRotation("Hips"))); * * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual glm::quat getJointRotation(const QString& name) const; /**jsdoc * Gets the translation of a joint relative to its parent, in model coordinates. *

Warning: These coordinates are not necessarily in meters.

*

For information on the joint hierarchy used, see * Avatar Standards.

* @function Avatar.getJointTranslation * @param {number} name - The name of the joint. * @returns {Vec3} The translation of the joint relative to its parent, in model coordinates. * @example Report the translation of your avatar's hips joint. * print(JSON.stringify(MyAvatar.getJointRotation("Hips"))); * * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual glm::vec3 getJointTranslation(const QString& name) const; /**jsdoc * Gets the rotations of all joints in the current avatar. Each joint's rotation is relative to its parent joint. * @function Avatar.getJointRotations * @returns {Quat[]} The rotations of all joints relative to each's parent. The values are in the same order as the array * returned by {@link MyAvatar.getJointNames}, or {@link Avatar.getJointNames} if using the Avatar API. * @example Report the rotations of all your avatar's joints. * print(JSON.stringify(MyAvatar.getJointRotations())); * * // Note: If using from the Avatar API, replace all "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual QVector getJointRotations() const; /**jsdoc * Gets the translations of all joints in the current avatar. Each joint's translation is relative to its parent joint, in * model coordinates. *

Warning: These coordinates are not necessarily in meters.

* @function Avatar.getJointTranslations * @returns {Vec3[]} The translations of all joints relative to each's parent, in model coordinates. The values are in the * same order as the array returned by {@link MyAvatar.getJointNames}, or {@link Avatar.getJointNames} if using the * Avatar API. */ Q_INVOKABLE virtual QVector getJointTranslations() const; /**jsdoc * Sets the rotations of all joints in the current avatar. Each joint's rotation is relative to its parent joint. *

Setting joint data completely overrides/replaces all motion from the default animation system including inverse * kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints, * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate * joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set * the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.

* @function Avatar.setJointRotations * @param {Quat[]} jointRotations - The rotations for all joints in the avatar. The values are in the same order as the * array returned by {@link MyAvatar.getJointNames}, or {@link Avatar.getJointNames} if using the Avatar API. * @example Set your avatar to its default T-pose then rotate its right arm.
* Avatar in T-pose * // Set all joint translations and rotations to defaults. * var i, length, rotation, translation; * for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) { * rotation = MyAvatar.getDefaultJointRotation(i); * translation = MyAvatar.getDefaultJointTranslation(i); * MyAvatar.setJointData(i, rotation, translation); * } * * // Get all join rotations. * var jointRotations = MyAvatar.getJointRotations(); * * // Update the rotation of the right arm in the array. * jointRotations[MyAvatar.getJointIndex("RightArm")] = { x: 0.47, y: 0.22, z: -0.02, w: 0.87 }; * * // Update all joint rotations. * MyAvatar.setJointRotations(jointRotations); * * // Restore your avatar's motion after 5s. * Script.setTimeout(function () { * MyAvatar.clearJointsData(); * }, 5000); * * // Note: If using from the Avatar API, replace all occurrences of "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual void setJointRotations(const QVector& jointRotations); /**jsdoc * Sets the translations of all joints in the current avatar. Each joint's translation is relative to its parent joint, in * model coordinates. *

Warning: These coordinates are not necessarily in meters.

*

Setting joint data completely overrides/replaces all motion from the default animation system including inverse * kinematics, but just for the specified joint. So for example, if you were to procedurally manipulate the finger joints, * the avatar's hand and head would still do inverse kinematics properly. However, as soon as you start to manipulate * joints in the inverse kinematics chain, the inverse kinematics might not function as you expect. For example, if you set * the rotation of the elbow, the hand inverse kinematics position won't end up in the right place.

* @function Avatar.setJointTranslations * @param {Vec3[]} translations - The translations for all joints in the avatar, in model coordinates. The values are in * the same order as the array returned by {@link MyAvatar.getJointNames}, or {@link Avatar.getJointNames} if using the * Avatar API. */ Q_INVOKABLE virtual void setJointTranslations(const QVector& jointTranslations); /**jsdoc * Clears all joint translations and rotations that have been set by script. This restores all motion from the default * animation system including inverse kinematics for all joints. * @function Avatar.clearJointsData * @example Set your avatar to it's default T-pose for a while. * // Set all joint translations and rotations to defaults. * var i, length, rotation, translation; * for (i = 0, length = MyAvatar.getJointNames().length; i < length; i++) { * rotation = MyAvatar.getDefaultJointRotation(i); * translation = MyAvatar.getDefaultJointTranslation(i); * MyAvatar.setJointData(i, rotation, translation); * } * * // Restore your avatar's motion after 5s. * Script.setTimeout(function () { * MyAvatar.clearJointsData(); * }, 5000); * * // Note: If using from the Avatar API, replace all occurrences of "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual void clearJointsData(); /**jsdoc * Gets the joint index for a named joint. The joint index value is the position of the joint in the array returned by * {@link MyAvatar.getJointNames}, or {@link Avatar.getJointNames} if using the Avatar API. * @function Avatar.getJointIndex * @param {string} name - The name of the joint. * @returns {number} The index of the joint if valid, otherwise -1. * @example Report the index of your avatar's left arm joint. * print(JSON.stringify(MyAvatar.getJointIndex("LeftArm"))); * * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ /// Returns the index of the joint with the specified name, or -1 if not found/unknown. Q_INVOKABLE virtual int getJointIndex(const QString& name) const; /**jsdoc * Gets the names of all the joints in the current avatar. * @function Avatar.getJointNames * @returns {string[]} The joint names. * @example Report the names of all the joints in your current avatar. * print(JSON.stringify(MyAvatar.getJointNames())); * * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual QStringList getJointNames() const; /**jsdoc * Sets the value of a blendshape to animate your avatar's face. To enable other users to see the resulting animation of * your avatar's face, set {@link Avatar.hasScriptedBlendshapes} to true while using this API and back to false when your * animation is complete. * @function Avatar.setBlendshape * @param {string} name - The name of the blendshape, per the * {@link https://docs.vircadia.dev/create/avatars/avatar-standards.html#blendshapes Avatar Standards}. * @param {number} value - A value between 0.0 and 1.0. * @example Open your avatar's mouth wide. * MyAvatar.hasScriptedBlendshapes = true; * MyAvatar.setBlendshape("JawOpen", 1.0); * * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ Q_INVOKABLE void setBlendshape(QString name, float val) { _headData->setBlendshape(name, val); } /**jsdoc * Gets information about the models currently attached to your avatar. * @function Avatar.getAttachmentsVariant * @returns {AttachmentData[]} Information about all models attached to your avatar. * @deprecated This function is deprecated and will be removed. Use avatar entities instead. */ // FIXME: Can this name be improved? Can it be deprecated? Q_INVOKABLE virtual QVariantList getAttachmentsVariant() const; /**jsdoc * Sets all models currently attached to your avatar. For example, if you retrieve attachment data using * {@link MyAvatar.getAttachmentsVariant} or {@link Avatar.getAttachmentsVariant}, make changes to it, and then want to * update your avatar's attachments per the changed data. * @function Avatar.setAttachmentsVariant * @param {AttachmentData[]} variant - The attachment data defining the models to have attached to your avatar. * @deprecated This function is deprecated and will be removed. Use avatar entities instead. */ // FIXME: Can this name be improved? Can it be deprecated? Q_INVOKABLE virtual void setAttachmentsVariant(const QVariantList& variant); virtual void storeAvatarEntityDataPayload(const QUuid& entityID, const QByteArray& payload); /**jsdoc * @function Avatar.updateAvatarEntity * @param {Uuid} entityID - The entity ID. * @param {Array.} entityData - Entity data. * @deprecated This function is deprecated and will be removed. */ Q_INVOKABLE virtual void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData); /**jsdoc * @function Avatar.clearAvatarEntity * @param {Uuid} entityID - The entity ID. * @param {boolean} [requiresRemovalFromTree=true] - Requires removal from tree. * @deprecated This function is deprecated and will be removed. */ Q_INVOKABLE virtual void clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFromTree = true); /**jsdoc *

Deprecated: This method is deprecated and will be removed.

* Use Avatar.hasScriptedBlendshapes property instead. * Enables blendshapes set using {@link Avatar.setBlendshape} or {@link MyAvatar.setBlendshape} to be transmitted to other * users so that they can see the animation of your avatar's face. * @function Avatar.setForceFaceTrackerConnected * @param {boolean} connected - true to enable blendshape changes to be transmitted to other users, * false to disable. */ Q_INVOKABLE void setForceFaceTrackerConnected(bool connected) { setHasScriptedBlendshapes(connected); } // key state void setKeyState(KeyState s) { _keyState = s; } KeyState keyState() const { return _keyState; } const HeadData* getHeadData() const { return _headData; } struct Identity { QVector attachmentData; QString displayName; QString sessionDisplayName; bool isReplicated; bool lookAtSnappingEnabled; AvatarDataPacket::IdentityFlags identityFlags; }; // identityChanged returns true if identity has changed, false otherwise. // identityChanged returns true if identity has changed, false otherwise. Similarly for displayNameChanged and skeletonModelUrlChange. void processAvatarIdentity(QDataStream& packetStream, bool& identityChanged, bool& displayNameChanged); QByteArray packTrait(AvatarTraits::TraitType traitType) const; QByteArray packTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID); void processTrait(AvatarTraits::TraitType traitType, QByteArray traitBinaryData); void processTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID, QByteArray traitBinaryData); void processDeletedTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID); void prepareResetTraitInstances(); QByteArray identityByteArray(bool setIsReplicated = false) const; QUrl getWireSafeSkeletonModelURL() const; virtual const QUrl& getSkeletonModelURL() const; const QString& getDisplayName() const { return _displayName; } const QString& getSessionDisplayName() const { return _sessionDisplayName; } bool getLookAtSnappingEnabled() const { return _lookAtSnappingEnabled; } /**jsdoc * Sets the avatar's skeleton model. * @function Avatar.setSkeletonModelURL * @param {string} url - The avatar's FST file. */ Q_INVOKABLE virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); virtual void setDisplayName(const QString& displayName); virtual void setSessionDisplayName(const QString& sessionDisplayName) { _sessionDisplayName = sessionDisplayName; markIdentityDataChanged(); } virtual bool isCertifyFailed() const { return _verificationFailed; } /**jsdoc * Gets information about the models currently attached to your avatar. * @function Avatar.getAttachmentData * @returns {AttachmentData[]} Information about all models attached to your avatar. * @deprecated This function is deprecated and will be removed. Use avatar entities instead. * @example Report the URLs of all current attachments. * var attachments = MyAvatar.getaAttachmentData(); * for (var i = 0; i < attachments.length; i++) { * print(attachments[i].modelURL); * } * * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual QVector getAttachmentData() const; /**jsdoc * Sets all models currently attached to your avatar. For example, if you retrieve attachment data using * {@link MyAvatar.getAttachmentData} or {@link Avatar.getAttachmentData}, make changes to it, and then want to update your avatar's attachments per the * changed data. You can also remove all attachments by using setting attachmentData to null. * @function Avatar.setAttachmentData * @param {AttachmentData[]} attachmentData - The attachment data defining the models to have attached to your avatar. Use * null to remove all attachments. * @deprecated This function is deprecated and will be removed. Use avatar entities instead. * @example Remove a hat attachment if your avatar is wearing it. * var hatURL = "https://s3.amazonaws.com/hifi-public/tony/cowboy-hat.fbx"; * var attachments = MyAvatar.getAttachmentData(); * * for (var i = 0; i < attachments.length; i++) { * if (attachments[i].modelURL === hatURL) { * attachments.splice(i, 1); * MyAvatar.setAttachmentData(attachments); * break; * } * } * * // Note: If using from the Avatar API, replace all occurrences of "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual void setAttachmentData(const QVector& attachmentData); /**jsdoc * Attaches a model to your avatar. For example, you can give your avatar a hat to wear, a guitar to hold, or a surfboard to * stand on. * @function Avatar.attach * @param {string} modelURL - The URL of the glTF, FBX, or OBJ model to attach. glTF models may be in JSON or binary format * (".gltf" or ".glb" URLs respectively). * @param {string} [jointName=""] - The name of the avatar joint (see {@link MyAvatar.getJointNames} or * {@link Avatar.getJointNames}) to attach the model to. * @param {Vec3} [translation=Vec3.ZERO] - The offset to apply to the model relative to the joint position. * @param {Quat} [rotation=Quat.IDENTITY] - The rotation to apply to the model relative to the joint orientation. * @param {number} [scale=1.0] - The scale to apply to the model. * @param {boolean} [isSoft=false] - If the model has a skeleton, set this to true so that the bones of the * attached model's skeleton are rotated to fit the avatar's current pose. isSoft is used, for example, * to have clothing that moves with the avatar. *

If true, the translation, rotation, and scale parameters are * ignored.

* @param {boolean} [allowDuplicates=false] - If true then more than one copy of any particular model may be * attached to the same joint; if false then the same model cannot be attached to the same joint. * @param {boolean} [useSaved=true] - Not used. * @deprecated This function is deprecated and will be removed. Use avatar entities instead. * @example Attach a cowboy hat to your avatar's head. * var attachment = { * modelURL: "https://s3.amazonaws.com/hifi-public/tony/cowboy-hat.fbx", * jointName: "Head", * translation: {"x": 0, "y": 0.25, "z": 0}, * rotation: {"x": 0, "y": 0, "z": 0, "w": 1}, * scale: 0.01, * isSoft: false * }; * * MyAvatar.attach(attachment.modelURL, * attachment.jointName, * attachment.translation, * attachment.rotation, * attachment.scale, * attachment.isSoft); * * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ Q_INVOKABLE virtual void attach(const QString& modelURL, const QString& jointName = QString(), const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f, bool isSoft = false, bool allowDuplicates = false, bool useSaved = true); /**jsdoc * Detaches the most recently attached instance of a particular model from either a specific joint or any joint. * @function Avatar.detachOne * @param {string} modelURL - The URL of the model to detach. * @param {string} [jointName=""] - The name of the joint to detach the model from. If "", then the most * recently attached model is removed from which ever joint it was attached to. * @deprecated This function is deprecated and will be removed. Use avatar entities instead. */ Q_INVOKABLE virtual void detachOne(const QString& modelURL, const QString& jointName = QString()); /**jsdoc * Detaches all instances of a particular model from either a specific joint or all joints. * @function Avatar.detachAll * @param {string} modelURL - The URL of the model to detach. * @param {string} [jointName=""] - The name of the joint to detach the model from. If "", then the model is * detached from all joints. * @deprecated This function is deprecated and will be removed. Use avatar entities instead. */ Q_INVOKABLE virtual void detachAll(const QString& modelURL, const QString& jointName = QString()); QString getSkeletonModelURLFromScript() const { return _skeletonModelURL.toString(); } void setSkeletonModelURLFromScript(const QString& skeletonModelString) { setSkeletonModelURL(QUrl(skeletonModelString)); } void setOwningAvatarMixer(const QWeakPointer& owningAvatarMixer) { _owningAvatarMixer = owningAvatarMixer; } int getAverageBytesReceivedPerSecond() const; int getReceiveRate() const; // An Avatar can be set Priority from the AvatarMixer side. bool getHasPriority() const { return _hasPriority; } // regular setHasPriority does a check of state changed and if true reset 'additionalFlagsChanged' timestamp void setHasPriority(bool hasPriority) { if (_hasPriority != hasPriority) { _additionalFlagsChanged = usecTimestampNow(); _hasPriority = hasPriority; } } // In some cases, we want to assign the hasPRiority flag without reseting timestamp void setHasPriorityWithoutTimestampReset(bool hasPriority) { _hasPriority = hasPriority; } const glm::vec3& getTargetVelocity() const { return _targetVelocity; } void clearRecordingBasis(); TransformPointer getRecordingBasis() const; void setRecordingBasis(TransformPointer recordingBasis = TransformPointer()); void createRecordingIDs(); virtual void avatarEntityDataToJson(QJsonObject& root) const; QJsonObject toJson() const; void fromJson(const QJsonObject& json, bool useFrameSkeleton = true); glm::vec3 getClientGlobalPosition() const { return _globalPosition; } AABox getGlobalBoundingBox() const { return AABox(_globalPosition + _globalBoundingBoxOffset - _globalBoundingBoxDimensions, _globalBoundingBoxDimensions); } AABox getDefaultBubbleBox() const; /**jsdoc * @comment Documented in derived classes' JSDoc because implementations are different. */ Q_INVOKABLE virtual AvatarEntityMap getAvatarEntityData() const; /**jsdoc * @comment Documented in derived classes' JSDoc because implementations are different. */ Q_INVOKABLE virtual void setAvatarEntityData(const AvatarEntityMap& avatarEntityData); void setAvatarEntityDataChanged(bool value) { _avatarEntityDataChanged = value; } AvatarEntityIDs getAndClearRecentlyRemovedIDs(); /**jsdoc * Gets the transform from the user's real world to the avatar's size, orientation, and position in the virtual world. * @function Avatar.getSensorToWorldMatrix * @returns {Mat4} The scale, rotation, and translation transform from the user's real world to the avatar's size, * orientation, and position in the virtual world. * @example Report the sensor to world matrix. * var sensorToWorldMatrix = MyAvatar.getSensorToWorldMatrix(); * print("Sensor to woprld matrix: " + JSON.stringify(sensorToWorldMatrix)); * print("Rotation: " + JSON.stringify(Mat4.extractRotation(sensorToWorldMatrix))); * print("Translation: " + JSON.stringify(Mat4.extractTranslation(sensorToWorldMatrix))); * print("Scale: " + JSON.stringify(Mat4.extractScale(sensorToWorldMatrix))); * * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ // thread safe Q_INVOKABLE glm::mat4 getSensorToWorldMatrix() const; /**jsdoc * Gets the scale that transforms dimensions in the user's real world to the avatar's size in the virtual world. * @function Avatar.getSensorToWorldScale * @returns {number} The scale that transforms dimensions in the user's real world to the avatar's size in the virtual * world. */ // thread safe Q_INVOKABLE float getSensorToWorldScale() const; /**jsdoc * Gets the rotation and translation of the left hand controller relative to the avatar. * @function Avatar.getControllerLeftHandMatrix * @returns {Mat4} The rotation and translation of the left hand controller relative to the avatar. * @example Report the left hand controller matrix. * var leftHandMatrix = MyAvatar.getControllerLeftHandMatrix(); * print("Controller left hand matrix: " + JSON.stringify(leftHandMatrix)); * print("Rotation: " + JSON.stringify(Mat4.extractRotation(leftHandMatrix))); * print("Translation: " + JSON.stringify(Mat4.extractTranslation(leftHandMatrix))); * print("Scale: " + JSON.stringify(Mat4.extractScale(leftHandMatrix))); // Always 1,1,1. * * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ // thread safe Q_INVOKABLE glm::mat4 getControllerLeftHandMatrix() const; /**jsdoc * Gets the rotation and translation of the right hand controller relative to the avatar. * @function Avatar.getControllerRightHandMatrix * @returns {Mat4} The rotation and translation of the right hand controller relative to the avatar. */ // thread safe Q_INVOKABLE glm::mat4 getControllerRightHandMatrix() const; /**jsdoc * Gets the amount of avatar mixer data being generated by the avatar. * @function Avatar.getDataRate * @param {AvatarDataRate} [rateName=""] - The type of avatar mixer data to get the data rate of. * @returns {number} The data rate in kbps. */ Q_INVOKABLE float getDataRate(const QString& rateName = QString("")) const; /**jsdoc * Gets the update rate of avatar mixer data being generated by the avatar. * @function Avatar.getUpdateRate * @param {AvatarUpdateRate} [rateName=""] - The type of avatar mixer data to get the update rate of. * @returns {number} The update rate in Hz. */ Q_INVOKABLE float getUpdateRate(const QString& rateName = QString("")) const; int getJointCount() const { return _jointData.size(); } QVector getLastSentJointData() { QReadLocker readLock(&_jointDataLock); _lastSentJointData.resize(_jointData.size()); return _lastSentJointData; } // A method intended to be overriden by MyAvatar for polling orientation for network transmission. virtual glm::quat getOrientationOutbound() const; // TODO: remove this HACK once we settle on optimal sort coefficients // These coefficients exposed for fine tuning the sort priority for transfering new _jointData to the render pipeline. static float _avatarSortCoefficientSize; static float _avatarSortCoefficientCenter; static float _avatarSortCoefficientAge; bool getIdentityDataChanged() const { return _identityDataChanged; } // has the identity data changed since the last time sendIdentityPacket() was called void markIdentityDataChanged() { _identityDataChanged = true; } void pushIdentitySequenceNumber() { ++_identitySequenceNumber; }; bool hasProcessedFirstIdentity() const { return _hasProcessedFirstIdentity; } float getDensity() const { return _density; } bool getIsReplicated() const { return _isReplicated; } void setReplicaIndex(int replicaIndex) { _replicaIndex = replicaIndex; } int getReplicaIndex() { return _replicaIndex; } static const float DEFAULT_BUBBLE_SCALE; /* = 2.4 */ AABox computeBubbleBox(float bubbleScale = DEFAULT_BUBBLE_SCALE) const; void setIsNewAvatar(bool isNewAvatar) { _isNewAvatar = isNewAvatar; } bool getIsNewAvatar() { return _isNewAvatar; } void setIsClientAvatar(bool isClientAvatar) { _isClientAvatar = isClientAvatar; } void setSkeletonData(const std::vector& skeletonData); std::vector getSkeletonData() const; void sendSkeletonData() const; QVector getJointData() const; glm::vec3 getHeadJointFrontVector() const; signals: /**jsdoc * Triggered when the avatar's displayName property value changes. * @function Avatar.displayNameChanged * @returns {Signal} * @example Report when your avatar display name changes. * MyAvatar.displayNameChanged.connect(function () { * print("Avatar display name changed to: " + MyAvatar.displayName); * }); * * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ void displayNameChanged(); /**jsdoc * Triggered when the avatar's sessionDisplayName property value changes. * @function Avatar.sessionDisplayNameChanged * @returns {Signal} * @example Report when your avatar's session display name changes. * MyAvatar.sessionDisplayNameChanged.connect(function () { * print("Avatar session display name changed to: " + MyAvatar.sessionDisplayName); * }); * * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ void sessionDisplayNameChanged(); /**jsdoc * Triggered when the avatar's model (i.e., skeletonModelURL property value) is changed. * @function Avatar.skeletonModelURLChanged * @returns {Signal} * @example Report when your avatar's skeleton model changes. * MyAvatar.skeletonModelURLChanged.connect(function () { * print("Skeleton model changed to: " + MyAvatar.skeletonModelURL); * }); * * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ void skeletonModelURLChanged(); /**jsdoc * Triggered when the avatar's lookAtSnappingEnabled property value changes. * @function Avatar.lookAtSnappingChanged * @param {boolean} enabled - true if look-at snapping is enabled, false if not. * @returns {Signal} * @example Report when your look-at snapping setting changes. * MyAvatar.lookAtSnappingChanged.connect(function () { * print("Avatar look-at snapping changed to: " + MyAvatar.lookAtSnappingEnabled); * }); * * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ void lookAtSnappingChanged(bool enabled); /**jsdoc * Triggered when the avatar's sessionUUID property value changes. * @function Avatar.sessionUUIDChanged * @returns {Signal} * @example Report when your avatar's session UUID changes. * MyAvatar.sessionUUIDChanged.connect(function () { * print("Avatar session UUID changed to: " + MyAvatar.sessionUUID); * }); * * // Note: If using from the Avatar API, replace "MyAvatar" with "Avatar". */ void sessionUUIDChanged(); public slots: /**jsdoc * @function Avatar.sendAvatarDataPacket * @param {boolean} [sendAll=false] - Send all. * @returns {number} * @deprecated This function is deprecated and will be removed. */ virtual int sendAvatarDataPacket(bool sendAll = false); /**jsdoc * @function Avatar.sendIdentityPacket * @returns {number} * @deprecated This function is deprecated and will be removed. */ int sendIdentityPacket(); /**jsdoc * @function Avatar.setSessionUUID * @param {Uuid} sessionUUID - Session UUID. * @deprecated This function is deprecated and will be removed. */ virtual void setSessionUUID(const QUuid& sessionUUID) { if (sessionUUID != getID()) { if (sessionUUID == QUuid()) { setID(AVATAR_SELF_ID); } else { setID(sessionUUID); } emit sessionUUIDChanged(); } } /**jsdoc * Gets the rotation of a joint relative to the avatar. *

Warning: Not able to be used in the Avatar API.

* @function Avatar.getAbsoluteJointRotationInObjectFrame * @param {number} index - The index of the joint. Not used. * @returns {Quat} Quat.IDENTITY. */ virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override; /**jsdoc * Gets the translation of a joint relative to the avatar. *

Warning: Not able to be used in the Avatar API.

* @function Avatar.getAbsoluteJointTranslationInObjectFrame * @param {number} index - The index of the joint. Not used. * @returns {Vec3} Vec3.ZERO. */ virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override; /**jsdoc * Sets the rotation of a joint relative to the avatar. *

Warning: Not able to be used in the Avatar API.

* @function Avatar.setAbsoluteJointRotationInObjectFrame * @param {number} index - The index of the joint. Not used. * @param {Quat} rotation - The rotation of the joint relative to the avatar. Not used. * @returns {boolean} false. */ virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override { return false; } /**jsdoc * Sets the translation of a joint relative to the avatar. *

Warning: Not able to be used in the Avatar API.

* @function Avatar.setAbsoluteJointTranslationInObjectFrame * @param {number} index - The index of the joint. Not used. * @param {Vec3} translation - The translation of the joint relative to the avatar. Not used. * @returns {boolean} false. */ virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) override { return false; } /**jsdoc * Gets the target scale of the avatar without any restrictions on permissible values imposed by the domain. In contrast, the * scale property's value may be limited by the domain's settings. * @function Avatar.getTargetScale * @returns {number} The target scale of the avatar. * @example Compare the target and current avatar scales. * print("Current avatar scale: " + MyAvatar.scale); * print("Target avatar scale: " + MyAvatar.getTargetScale()); * * // Note: If using from the Avatar API, replace all occurrences of "MyAvatar" with "Avatar". */ float getTargetScale() const { return _targetScale; } // why is this a slot? /**jsdoc * @function Avatar.resetLastSent * @deprecated This function is deprecated and will be removed. */ void resetLastSent() { _lastToByteArray = 0; } protected: void insertRemovedEntityID(const QUuid entityID); void lazyInitHeadData() const; float getDistanceBasedMinRotationDOT(glm::vec3 viewerPosition) const; float getDistanceBasedMinTranslationDistance(glm::vec3 viewerPosition) const; bool avatarBoundingBoxChangedSince(quint64 time) const { return _avatarBoundingBoxChanged >= time; } bool avatarScaleChangedSince(quint64 time) const { return _avatarScaleChanged >= time; } bool lookAtPositionChangedSince(quint64 time) const { return _headData->lookAtPositionChangedSince(time); } bool sensorToWorldMatrixChangedSince(quint64 time) const { return _sensorToWorldMatrixChanged >= time; } bool additionalFlagsChangedSince(quint64 time) const { return _additionalFlagsChanged >= time; } bool parentInfoChangedSince(quint64 time) const { return _parentChanged >= time; } bool faceTrackerInfoChangedSince(quint64 time) const { return true; } // FIXME bool hasParent() const { return !getParentID().isNull(); } 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. bool _isReplicated{ false }; glm::vec3 _handPosition; virtual const QString& getSessionDisplayNameForTransport() const { return _sessionDisplayName; } virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) { } // No-op in AvatarMixer // Body scale float _targetScale; float _domainMinimumHeight { MIN_AVATAR_HEIGHT }; float _domainMaximumHeight { MAX_AVATAR_HEIGHT }; // Hand state (are we grabbing something or not) char _handState; QVector _jointData; ///< the state of the skeleton joints QVector _lastSentJointData; ///< the state of the skeleton joints last time we transmitted mutable QReadWriteLock _jointDataLock; // key state KeyState _keyState; bool _hasNewJointData { true }; // set in AvatarData, cleared in Avatar mutable HeadData* _headData { nullptr }; QUrl _skeletonModelURL; QVector _attachmentData; QVector _oldAttachmentData; QString _displayName; QString _sessionDisplayName { }; bool _lookAtSnappingEnabled { true }; bool _verificationFailed { false }; quint64 _errorLogExpiry; ///< time in future when to log an error QWeakPointer _owningAvatarMixer; glm::vec3 _targetVelocity; SimpleMovingAverage _averageBytesReceived; // During recording, this holds the starting position, orientation & scale of the recorded avatar // During playback, it holds the origin from which to play the relative positions in the clip TransformPointer _recordingBasis; // _globalPosition is sent along with localPosition + parent because the avatar-mixer doesn't know // where Entities are located. This is currently only used by the mixer to decide how often to send // updates about one avatar to another. glm::vec3 _globalPosition { 0, 0, 0 }; glm::vec3 _serverPosition { 0, 0, 0 }; quint64 _globalPositionChanged { 0 }; quint64 _avatarBoundingBoxChanged { 0 }; quint64 _avatarScaleChanged { 0 }; quint64 _sensorToWorldMatrixChanged { 0 }; quint64 _additionalFlagsChanged { 0 }; quint64 _parentChanged { 0 }; quint64 _lastToByteArray { 0 }; // tracks the last time we did a toByteArray // Some rate data for incoming data in bytes RateCounter<> _parseBufferRate; RateCounter<> _globalPositionRate; RateCounter<> _localPositionRate; RateCounter<> _handControllersRate; RateCounter<> _avatarBoundingBoxRate; RateCounter<> _avatarOrientationRate; RateCounter<> _avatarScaleRate; RateCounter<> _lookAtPositionRate; RateCounter<> _audioLoudnessRate; RateCounter<> _sensorToWorldRate; RateCounter<> _additionalFlagsRate; RateCounter<> _parentInfoRate; RateCounter<> _faceTrackerRate; RateCounter<> _jointDataRate; RateCounter<> _jointDefaultPoseFlagsRate; RateCounter<> _farGrabJointRate; // Some rate data for incoming data updates RateCounter<> _parseBufferUpdateRate; RateCounter<> _globalPositionUpdateRate; RateCounter<> _localPositionUpdateRate; RateCounter<> _handControllersUpdateRate; RateCounter<> _avatarBoundingBoxUpdateRate; RateCounter<> _avatarOrientationUpdateRate; RateCounter<> _avatarScaleUpdateRate; RateCounter<> _lookAtPositionUpdateRate; RateCounter<> _audioLoudnessUpdateRate; RateCounter<> _sensorToWorldUpdateRate; RateCounter<> _additionalFlagsUpdateRate; RateCounter<> _parentInfoUpdateRate; RateCounter<> _faceTrackerUpdateRate; RateCounter<> _jointDataUpdateRate; RateCounter<> _jointDefaultPoseFlagsUpdateRate; RateCounter<> _farGrabJointUpdateRate; // Some rate data for outgoing data AvatarDataRate _outboundDataRate; glm::vec3 _globalBoundingBoxDimensions; glm::vec3 _globalBoundingBoxOffset; AABox _defaultBubbleBox; AABox _fitBoundingBox; mutable ReadWriteLockable _avatarEntitiesLock; AvatarEntityIDs _avatarEntityRemoved; // recently removed AvatarEntity ids AvatarEntityIDs _avatarEntityForRecording; // create new entities id for avatar recording PackedAvatarEntityMap _packedAvatarEntityData; bool _avatarEntityDataChanged { false }; mutable ReadWriteLockable _avatarGrabsLock; AvatarGrabDataMap _avatarGrabData; bool _avatarGrabDataChanged { false }; // by network mutable ReadWriteLockable _avatarSkeletonDataLock; std::vector _avatarSkeletonData; // used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers. ThreadSafeValueCache _sensorToWorldMatrixCache { glm::mat4() }; ThreadSafeValueCache _controllerLeftHandMatrixCache { glm::mat4() }; ThreadSafeValueCache _controllerRightHandMatrixCache { glm::mat4() }; ThreadSafeValueCache _farGrabRightMatrixCache { glm::mat4() }; ThreadSafeValueCache _farGrabLeftMatrixCache { glm::mat4() }; ThreadSafeValueCache _farGrabMouseMatrixCache { glm::mat4() }; ThreadSafeValueCache _collisionCapsuleCache{ QVariantMap() }; int getFauxJointIndex(const QString& name) const; float _audioLoudness { 0.0f }; quint64 _audioLoudnessChanged { 0 }; float _audioAverageLoudness { 0.0f }; bool _identityDataChanged { false }; udt::SequenceNumber _identitySequenceNumber { 0 }; bool _hasProcessedFirstIdentity { false }; float _density; int _replicaIndex { 0 }; bool _isNewAvatar { true }; bool _isClientAvatar { false }; bool _collideWithOtherAvatars { true }; bool _hasPriority{ false }; // null unless MyAvatar or ScriptableAvatar sending traits data to mixer std::unique_ptr _clientTraitsHandler; template T readLockWithNamedJointIndex(const QString& name, const T& defaultValue, F f) const { QReadLocker readLock(&_jointDataLock); int index = getJointIndex(name); if (index == -1) { return defaultValue; } return f(index); } template T readLockWithNamedJointIndex(const QString& name, F f) const { return readLockWithNamedJointIndex(name, T(), f); } template void writeLockWithNamedJointIndex(const QString& name, F f) { QWriteLocker writeLock(&_jointDataLock); int index = getJointIndex(name); if (index == -1) { return; } if (_jointData.size() <= index) { _jointData.resize(index + 1); } f(index); } bool updateAvatarGrabData(const QUuid& grabID, const QByteArray& grabData); virtual void clearAvatarGrabData(const QUuid& grabID); private: friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar); static QUrl _defaultFullAvatarModelUrl; // privatize the copy constructor and assignment operator so they cannot be called AvatarData(const AvatarData&); AvatarData& operator= (const AvatarData&); }; Q_DECLARE_METATYPE(AvatarData*) QJsonValue toJsonValue(const JointData& joint); JointData jointDataFromJsonValue(const QJsonValue& q); class AttachmentData { public: QUrl modelURL; QString jointName; glm::vec3 translation; glm::quat rotation; float scale { 1.0f }; bool isSoft { false }; bool isValid() const { return modelURL.isValid(); } bool operator==(const AttachmentData& other) const; QJsonObject toJson() const; void fromJson(const QJsonObject& json); QVariant toVariant() const; bool fromVariant(const QVariant& variant); }; QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment); QDataStream& operator>>(QDataStream& in, AttachmentData& attachment); Q_DECLARE_METATYPE(AttachmentData) Q_DECLARE_METATYPE(QVector) /// Scriptable wrapper for attachments. class AttachmentDataObject : public QObject, protected QScriptable { Q_OBJECT Q_PROPERTY(QString modelURL READ getModelURL WRITE setModelURL) Q_PROPERTY(QString jointName READ getJointName WRITE setJointName) Q_PROPERTY(glm::vec3 translation READ getTranslation WRITE setTranslation) Q_PROPERTY(glm::quat rotation READ getRotation WRITE setRotation) Q_PROPERTY(float scale READ getScale WRITE setScale) Q_PROPERTY(bool isSoft READ getIsSoft WRITE setIsSoft) public: Q_INVOKABLE void setModelURL(const QString& modelURL); Q_INVOKABLE QString getModelURL() const; Q_INVOKABLE void setJointName(const QString& jointName); Q_INVOKABLE QString getJointName() const; Q_INVOKABLE void setTranslation(const glm::vec3& translation); Q_INVOKABLE glm::vec3 getTranslation() const; Q_INVOKABLE void setRotation(const glm::quat& rotation); Q_INVOKABLE glm::quat getRotation() const; Q_INVOKABLE void setScale(float scale); Q_INVOKABLE float getScale() const; Q_INVOKABLE void setIsSoft(bool scale); Q_INVOKABLE bool getIsSoft() const; }; void registerAvatarTypes(QScriptEngine* engine); class RayToAvatarIntersectionResult { public: bool intersects { false }; QUuid avatarID; float distance { FLT_MAX }; BoxFace face; glm::vec3 intersection; glm::vec3 surfaceNormal; int jointIndex { -1 }; QVariantMap extraInfo; }; Q_DECLARE_METATYPE(RayToAvatarIntersectionResult) QScriptValue RayToAvatarIntersectionResultToScriptValue(QScriptEngine* engine, const RayToAvatarIntersectionResult& results); void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, RayToAvatarIntersectionResult& results); class ParabolaToAvatarIntersectionResult { public: bool intersects { false }; QUuid avatarID; float distance { 0.0f }; float parabolicDistance { 0.0f }; BoxFace face; glm::vec3 intersection; glm::vec3 surfaceNormal; QVariantMap extraInfo; }; Q_DECLARE_METATYPE(AvatarEntityMap) QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value); void AvatarEntityMapFromScriptValue(const QScriptValue& object, AvatarEntityMap& value); // faux joint indexes (-1 means invalid) const int NO_JOINT_INDEX = 65535; // -1 const int SENSOR_TO_WORLD_MATRIX_INDEX = 65534; // -2 const int CONTROLLER_RIGHTHAND_INDEX = 65533; // -3 const int CONTROLLER_LEFTHAND_INDEX = 65532; // -4 const int CAMERA_RELATIVE_CONTROLLER_RIGHTHAND_INDEX = 65531; // -5 const int CAMERA_RELATIVE_CONTROLLER_LEFTHAND_INDEX = 65530; // -6 const int CAMERA_MATRIX_INDEX = 65529; // -7 const int FARGRAB_RIGHTHAND_INDEX = 65528; // -8 const int FARGRAB_LEFTHAND_INDEX = 65527; // -9 const int FARGRAB_MOUSE_INDEX = 65526; // -10 const int LOWEST_PSEUDO_JOINT_INDEX = 65526; const int MAX_NUM_AVATAR_GRABS = 6; #endif // hifi_AvatarData_h