Merge pull request #14187 from luiscuenca/transitAddAnimation

Add animations to Avatar Transit
This commit is contained in:
Luis Cuenca 2018-10-24 10:51:21 -07:00 committed by GitHub
commit fac9188dfb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 422 additions and 111 deletions

View file

@ -0,0 +1,140 @@
{
"version": "1.1",
"root": {
"id": "userAnimStateMachine",
"type": "stateMachine",
"data": {
"currentState": "idleAnim",
"states": [
{
"id": "idleAnim",
"interpTarget": 6,
"interpDuration": 6,
"transitions": [
{ "var": "postTransitAnim", "state": "postTransitAnim" },
{ "var": "preTransitAnim", "state": "preTransitAnim" }
]
},
{
"id": "preTransitAnim",
"interpTarget": 6,
"interpDuration": 6,
"transitions": [
{ "var": "idleAnim", "state": "idleAnim" },
{ "var": "transitAnim", "state": "transitAnim" }
]
},
{
"id": "transitAnim",
"interpTarget": 6,
"interpDuration": 6,
"transitions": [
{ "var": "preTransitAnim", "state": "preTransitAnim" },
{ "var": "postTransitAnim", "state": "postTransitAnim" }
]
},
{
"id": "postTransitAnim",
"interpTarget": 6,
"interpDuration": 6,
"transitions": [
{ "var": "transitAnim", "state": "transitAnim" },
{ "var": "idleAnim", "state": "idleAnim" }
]
},
{
"id": "userAnimA",
"interpTarget": 6,
"interpDuration": 6,
"transitions": [
{ "var": "idleAnim", "state": "idleAnim" },
{ "var": "userAnimB", "state": "userAnimB" }
]
},
{
"id": "userAnimB",
"interpTarget": 6,
"interpDuration": 6,
"transitions": [
{ "var": "idleAnim", "state": "idleAnim" },
{ "var": "userAnimA", "state": "userAnimA" }
]
}
]
},
"children": [
{
"id": "idleAnim",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/idle.fbx",
"startFrame": 0.0,
"endFrame": 90.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
},
{
"id": "preTransitAnim",
"type": "clip",
"data": {
"url": "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx",
"startFrame": 0.0,
"endFrame": 10.0,
"timeScale": 1.0,
"loopFlag": false
},
"children": []
},
{
"id": "transitAnim",
"type": "clip",
"data": {
"url": "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx",
"startFrame": 11.0,
"endFrame": 11.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
},
{
"id": "postTransitAnim",
"type": "clip",
"data": {
"url": "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx",
"startFrame": 22.0,
"endFrame": 49.0,
"timeScale": 1.0,
"loopFlag": false
},
"children": []
},
{
"id": "userAnimA",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/idle.fbx",
"startFrame": 0.0,
"endFrame": 90.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
},
{
"id": "userAnimB",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/idle.fbx",
"startFrame": 0.0,
"endFrame": 90.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
}
]
}
}

View file

@ -135,7 +135,7 @@ bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm::
glm::vec3 palmPosition;
glm::quat palmRotation;
bool isTransitingWithAvatar = holdingAvatar->getTransit()->isTransiting();
bool isTransitingWithAvatar = holdingAvatar->getTransit()->isActive();
if (isTransitingWithAvatar != _isTransitingWithAvatar) {
_isTransitingWithAvatar = isTransitingWithAvatar;
auto ownerEntity = _ownerEntity.lock();
@ -424,7 +424,7 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
if (ownerEntity) {
ownerEntity->setDynamicDataDirty(true);
ownerEntity->setDynamicDataNeedsTransmit(true);
ownerEntity->setTransitingWithAvatar(myAvatar->getTransit()->isTransiting());
ownerEntity->setTransitingWithAvatar(myAvatar->getTransit()->isActive());
}
});
}

View file

@ -71,14 +71,12 @@ AvatarManager::AvatarManager(QObject* parent) :
}
});
const float AVATAR_TRANSIT_TRIGGER_DISTANCE = 1.0f;
const int AVATAR_TRANSIT_FRAME_COUNT = 11; // Based on testing
const int AVATAR_TRANSIT_FRAMES_PER_METER = 1; // Based on testing
_transitConfig._totalFrames = AVATAR_TRANSIT_FRAME_COUNT;
_transitConfig._triggerDistance = AVATAR_TRANSIT_TRIGGER_DISTANCE;
_transitConfig._minTriggerDistance = AVATAR_TRANSIT_MIN_TRIGGER_DISTANCE;
_transitConfig._maxTriggerDistance = AVATAR_TRANSIT_MAX_TRIGGER_DISTANCE;
_transitConfig._framesPerMeter = AVATAR_TRANSIT_FRAMES_PER_METER;
_transitConfig._isDistanceBased = true;
_transitConfig._isDistanceBased = AVATAR_TRANSIT_DISTANCE_BASED;
_transitConfig._abortDistance = AVATAR_TRANSIT_ABORT_DISTANCE;
}
AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) {
@ -126,13 +124,39 @@ void AvatarManager::setSpace(workload::SpacePointer& space ) {
_space = space;
}
void AvatarManager::handleTransitAnimations(AvatarTransit::Status status) {
switch (status) {
case AvatarTransit::Status::STARTED:
_myAvatar->getSkeletonModel()->getRig().triggerNetworkAnimation("preTransitAnim");
break;
case AvatarTransit::Status::START_TRANSIT:
_myAvatar->getSkeletonModel()->getRig().triggerNetworkAnimation("transitAnim");
break;
case AvatarTransit::Status::END_TRANSIT:
_myAvatar->getSkeletonModel()->getRig().triggerNetworkAnimation("postTransitAnim");
break;
case AvatarTransit::Status::ENDED:
_myAvatar->getSkeletonModel()->getRig().triggerNetworkAnimation("idleAnim");
break;
case AvatarTransit::Status::PRE_TRANSIT:
break;
case AvatarTransit::Status::POST_TRANSIT:
break;
case AvatarTransit::Status::IDLE:
break;
case AvatarTransit::Status::TRANSITING:
break;
case AvatarTransit::Status::ABORT_TRANSIT:
break;
}
}
void AvatarManager::updateMyAvatar(float deltaTime) {
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showWarnings, "AvatarManager::updateMyAvatar()");
AvatarTransit::Status status = _myAvatar->updateTransit(deltaTime, _myAvatar->getNextPosition(), _transitConfig);
bool sendFirstTransitPackage = (status == AvatarTransit::Status::START_TRANSIT);
bool blockTransitData = (status == AvatarTransit::Status::TRANSITING);
AvatarTransit::Status status = _myAvatar->updateTransit(deltaTime, _myAvatar->getNextPosition(), _myAvatar->getSensorToWorldScale(), _transitConfig);
handleTransitAnimations(status);
_myAvatar->update(deltaTime);
render::Transaction transaction;
@ -142,18 +166,13 @@ void AvatarManager::updateMyAvatar(float deltaTime) {
quint64 now = usecTimestampNow();
quint64 dt = now - _lastSendAvatarDataTime;
if (sendFirstTransitPackage || (dt > MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS && !_myAvatarDataPacketsPaused && !blockTransitData)) {
if (dt > MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS && !_myAvatarDataPacketsPaused) {
// send head/hand data to the avatar mixer and voxel server
PerformanceTimer perfTimer("send");
if (sendFirstTransitPackage) {
_myAvatar->overrideNextPackagePositionData(_myAvatar->getTransit()->getEndPosition());
}
PerformanceTimer perfTimer("send");
_myAvatar->sendAvatarDataPacket();
_lastSendAvatarDataTime = now;
_myAvatarSendRate.increment();
}
}
@ -267,7 +286,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
if (inView && avatar->hasNewJointData()) {
numAvatarsUpdated++;
}
auto transitStatus = avatar->_transit.update(deltaTime, avatar->_globalPosition, _transitConfig);
auto transitStatus = avatar->_transit.update(deltaTime, avatar->_serverPosition, _transitConfig);
if (avatar->getIsNewAvatar() && (transitStatus == AvatarTransit::Status::START_TRANSIT || transitStatus == AvatarTransit::Status::ABORT_TRANSIT)) {
avatar->_transit.reset();
avatar->setIsNewAvatar(false);

View file

@ -221,6 +221,7 @@ private:
// frequently grabs a read lock on the hash to get a given avatar by ID
void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar,
KillAvatarReason removalReason = KillAvatarReason::NoReason) override;
void handleTransitAnimations(AvatarTransit::Status status);
QVector<AvatarSharedPointer> _avatarsToFade;

View file

@ -1058,7 +1058,6 @@ void MyAvatar::updateSensorToWorldMatrix() {
updateJointFromController(controller::Action::RIGHT_HAND, _controllerRightHandMatrixCache);
if (hasSensorToWorldScaleChanged) {
setTransitScale(sensorToWorldScale);
emit sensorToWorldScaleChanged(sensorToWorldScale);
}

View file

@ -51,6 +51,8 @@ public:
bool getMirrorFlag() const { return _mirrorFlag; }
void setMirrorFlag(bool mirrorFlag) { _mirrorFlag = mirrorFlag; }
float getFrame() const { return _frame; }
void loadURL(const QString& url);
protected:

View file

@ -31,6 +31,7 @@
#include "AnimSkeleton.h"
#include "AnimUtil.h"
#include "IKTarget.h"
#include "PathUtils.h"
static int nextRigId = 1;
@ -133,6 +134,30 @@ void Rig::overrideAnimation(const QString& url, float fps, bool loop, float firs
_animVars.set("userAnimB", clipNodeEnum == UserAnimState::B);
}
void Rig::triggerNetworkAnimation(const QString& animName) {
_networkVars.set("idleAnim", false);
_networkVars.set("preTransitAnim", false);
_networkVars.set("transitAnim", false);
_networkVars.set("postTransitAnim", false);
_sendNetworkNode = true;
if (animName == "idleAnim") {
_networkVars.set("idleAnim", true);
_networkAnimState.clipNodeEnum = NetworkAnimState::Idle;
_sendNetworkNode = false;
} else if (animName == "preTransitAnim") {
_networkVars.set("preTransitAnim", true);
_networkAnimState.clipNodeEnum = NetworkAnimState::PreTransit;
} else if (animName == "transitAnim") {
_networkVars.set("transitAnim", true);
_networkAnimState.clipNodeEnum = NetworkAnimState::Transit;
} else if (animName == "postTransitAnim") {
_networkVars.set("postTransitAnim", true);
_networkAnimState.clipNodeEnum = NetworkAnimState::PostTransit;
}
}
void Rig::restoreAnimation() {
if (_userAnimState.clipNodeEnum != UserAnimState::None) {
_userAnimState.clipNodeEnum = UserAnimState::None;
@ -144,6 +169,16 @@ void Rig::restoreAnimation() {
}
}
void Rig::restoreNetworkAnimation() {
if (_networkAnimState.clipNodeEnum != NetworkAnimState::Idle) {
_networkAnimState.clipNodeEnum = NetworkAnimState::Idle;
_networkVars.set("idleAnim", true);
_networkVars.set("preTransitAnim", false);
_networkVars.set("transitAnim", false);
_networkVars.set("postTransitAnim", false);
}
}
QStringList Rig::getAnimationRoles() const {
if (_animNode) {
QStringList list;
@ -208,11 +243,17 @@ void Rig::restoreRoleAnimation(const QString& role) {
void Rig::destroyAnimGraph() {
_animSkeleton.reset();
_animLoader.reset();
_networkLoader.reset();
_animNode.reset();
_internalPoseSet._relativePoses.clear();
_internalPoseSet._absolutePoses.clear();
_internalPoseSet._overridePoses.clear();
_internalPoseSet._overrideFlags.clear();
_networkNode.reset();
_networkPoseSet._relativePoses.clear();
_networkPoseSet._absolutePoses.clear();
_networkPoseSet._overridePoses.clear();
_networkPoseSet._overrideFlags.clear();
_numOverrides = 0;
_leftEyeJointChildren.clear();
_rightEyeJointChildren.clear();
@ -229,14 +270,24 @@ void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOff
_internalPoseSet._relativePoses.clear();
_internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses();
_networkPoseSet._relativePoses.clear();
_networkPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses();
buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses);
buildAbsoluteRigPoses(_networkPoseSet._relativePoses, _networkPoseSet._absolutePoses);
_internalPoseSet._overridePoses.clear();
_internalPoseSet._overridePoses = _animSkeleton->getRelativeDefaultPoses();
_internalPoseSet._overrideFlags.clear();
_internalPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints(), false);
_networkPoseSet._overridePoses.clear();
_networkPoseSet._overridePoses = _animSkeleton->getRelativeDefaultPoses();
_networkPoseSet._overrideFlags.clear();
_networkPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints(), false);
_numOverrides = 0;
buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses);
@ -270,6 +321,18 @@ void Rig::reset(const FBXGeometry& geometry) {
_internalPoseSet._overrideFlags.clear();
_internalPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints(), false);
_networkPoseSet._relativePoses.clear();
_networkPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses();
buildAbsoluteRigPoses(_networkPoseSet._relativePoses, _networkPoseSet._absolutePoses);
_networkPoseSet._overridePoses.clear();
_networkPoseSet._overridePoses = _animSkeleton->getRelativeDefaultPoses();
_networkPoseSet._overrideFlags.clear();
_networkPoseSet._overrideFlags.resize(_animSkeleton->getNumJoints(), false);
_numOverrides = 0;
buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses);
@ -1049,26 +1112,56 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons
updateAnimationStateHandlers();
_animVars.setRigToGeometryTransform(_rigToGeometryTransform);
if (_networkNode) {
_networkVars.setRigToGeometryTransform(_rigToGeometryTransform);
}
AnimContext context(_enableDebugDrawIKTargets, _enableDebugDrawIKConstraints, _enableDebugDrawIKChains,
getGeometryToRigTransform(), rigToWorldTransform);
// evaluate the animation
AnimVariantMap triggersOut;
AnimVariantMap networkTriggersOut;
_internalPoseSet._relativePoses = _animNode->evaluate(_animVars, context, deltaTime, triggersOut);
if (_networkNode) {
_networkPoseSet._relativePoses = _networkNode->evaluate(_networkVars, context, deltaTime, networkTriggersOut);
const float NETWORK_ANIMATION_BLEND_FRAMES = 6.0f;
float alpha = 1.0f;
std::shared_ptr<AnimClip> clip;
if (_networkAnimState.clipNodeEnum == NetworkAnimState::PreTransit) {
clip = std::dynamic_pointer_cast<AnimClip>(_networkNode->findByName("preTransitAnim"));
if (clip) {
alpha = (clip->getFrame() - clip->getStartFrame()) / NETWORK_ANIMATION_BLEND_FRAMES;
}
} else if (_networkAnimState.clipNodeEnum == NetworkAnimState::PostTransit) {
clip = std::dynamic_pointer_cast<AnimClip>(_networkNode->findByName("postTransitAnim"));
if (clip) {
alpha = (clip->getEndFrame() - clip->getFrame()) / NETWORK_ANIMATION_BLEND_FRAMES;
}
}
if (_sendNetworkNode) {
for (size_t i = 0; i < _networkPoseSet._relativePoses.size(); i++) {
_networkPoseSet._relativePoses[i].blend(_internalPoseSet._relativePoses[i], (alpha > 1.0f ? 1.0f : alpha));
}
}
}
if ((int)_internalPoseSet._relativePoses.size() != _animSkeleton->getNumJoints()) {
// animations haven't fully loaded yet.
_internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses();
}
if ((int)_networkPoseSet._relativePoses.size() != _animSkeleton->getNumJoints()) {
// animations haven't fully loaded yet.
_networkPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses();
}
_lastAnimVars = _animVars;
_animVars.clearTriggers();
_animVars = triggersOut;
_networkVars.clearTriggers();
_networkVars = networkTriggersOut;
_lastContext = context;
}
applyOverridePoses();
buildAbsoluteRigPoses(_internalPoseSet._relativePoses, _internalPoseSet._absolutePoses);
buildAbsoluteRigPoses(_networkPoseSet._relativePoses, _networkPoseSet._absolutePoses);
// copy internal poses to external poses
{
QWriteLocker writeLock(&_externalPoseSetLock);
@ -1672,11 +1765,14 @@ void Rig::initAnimGraph(const QUrl& url) {
_animGraphURL = url;
_animNode.reset();
_networkNode.reset();
// load the anim graph
_animLoader.reset(new AnimNodeLoader(url));
auto networkUrl = PathUtils::resourcesUrl("avatar/network-animation.json");
_networkLoader.reset(new AnimNodeLoader(networkUrl));
std::weak_ptr<AnimSkeleton> weakSkeletonPtr = _animSkeleton;
connect(_animLoader.get(), &AnimNodeLoader::success, [this, weakSkeletonPtr](AnimNode::Pointer nodeIn) {
connect(_animLoader.get(), &AnimNodeLoader::success, [this, weakSkeletonPtr, url](AnimNode::Pointer nodeIn) {
_animNode = nodeIn;
// abort load if the previous skeleton was deleted.
@ -1703,7 +1799,33 @@ void Rig::initAnimGraph(const QUrl& url) {
emit onLoadComplete();
});
connect(_animLoader.get(), &AnimNodeLoader::error, [url](int error, QString str) {
qCCritical(animation) << "Error loading" << url.toDisplayString() << "code = " << error << "str =" << str;
qCritical(animation) << "Error loading" << url.toDisplayString() << "code = " << error << "str =" << str;
});
connect(_networkLoader.get(), &AnimNodeLoader::success, [this, weakSkeletonPtr, networkUrl](AnimNode::Pointer nodeIn) {
_networkNode = nodeIn;
// abort load if the previous skeleton was deleted.
auto sharedSkeletonPtr = weakSkeletonPtr.lock();
if (!sharedSkeletonPtr) {
return;
}
_networkNode->setSkeleton(sharedSkeletonPtr);
if (_networkAnimState.clipNodeEnum != NetworkAnimState::Idle) {
// restore the user animation we had before reset.
NetworkAnimState origState = _networkAnimState;
_networkAnimState = { NetworkAnimState::Idle, "", 30.0f, false, 0.0f, 0.0f };
if (_networkAnimState.clipNodeEnum == NetworkAnimState::PreTransit) {
triggerNetworkAnimation("preTransitAnim");
} else if (_networkAnimState.clipNodeEnum == NetworkAnimState::Transit) {
triggerNetworkAnimation("transitAnim");
} else if (_networkAnimState.clipNodeEnum == NetworkAnimState::PostTransit) {
triggerNetworkAnimation("postTransitAnim");
}
}
});
connect(_networkLoader.get(), &AnimNodeLoader::error, [networkUrl](int error, QString str) {
qCritical(animation) << "Error loading" << networkUrl.toDisplayString() << "code = " << error << "str =" << str;
});
}
}
@ -1782,13 +1904,13 @@ void Rig::copyJointsIntoJointData(QVector<JointData>& jointDataVec) const {
if (isIndexValid(i)) {
// rotations are in absolute rig frame.
glm::quat defaultAbsRot = geometryToRigPose.rot() * _animSkeleton->getAbsoluteDefaultPose(i).rot();
data.rotation = _internalPoseSet._absolutePoses[i].rot();
data.rotation = !_sendNetworkNode ? _internalPoseSet._absolutePoses[i].rot() : _networkPoseSet._absolutePoses[i].rot();
data.rotationIsDefaultPose = isEqual(data.rotation, defaultAbsRot);
// translations are in relative frame but scaled so that they are in meters,
// instead of geometry units.
glm::vec3 defaultRelTrans = _geometryOffset.scale() * _animSkeleton->getRelativeDefaultPose(i).trans();
data.translation = _geometryOffset.scale() * _internalPoseSet._relativePoses[i].trans();
data.translation = _geometryOffset.scale() * (!_sendNetworkNode ? _internalPoseSet._relativePoses[i].trans() : _networkPoseSet._relativePoses[i].trans());
data.translationIsDefaultPose = isEqual(data.translation, defaultRelTrans);
} else {
data.translationIsDefaultPose = true;

View file

@ -113,7 +113,10 @@ public:
void destroyAnimGraph();
void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
void triggerNetworkAnimation(const QString& animName);
void restoreAnimation();
void restoreNetworkAnimation();
QStringList getAnimationRoles() const;
void overrideRoleAnimation(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
void restoreRoleAnimation(const QString& role);
@ -269,6 +272,7 @@ protected:
// Only accessed by the main thread
PoseSet _internalPoseSet;
PoseSet _networkPoseSet;
// Copy of the _poseSet for external threads.
PoseSet _externalPoseSet;
@ -300,9 +304,12 @@ protected:
QUrl _animGraphURL;
std::shared_ptr<AnimNode> _animNode;
std::shared_ptr<AnimNode> _networkNode;
std::shared_ptr<AnimSkeleton> _animSkeleton;
std::unique_ptr<AnimNodeLoader> _animLoader;
std::unique_ptr<AnimNodeLoader> _networkLoader;
AnimVariantMap _animVars;
AnimVariantMap _networkVars;
enum class RigRole {
Idle = 0,
@ -315,6 +322,25 @@ protected:
RigRole _state { RigRole::Idle };
RigRole _desiredState { RigRole::Idle };
float _desiredStateAge { 0.0f };
struct NetworkAnimState {
enum ClipNodeEnum {
Idle = 0,
PreTransit,
Transit,
PostTransit
};
NetworkAnimState() : clipNodeEnum(NetworkAnimState::Idle) {}
NetworkAnimState(ClipNodeEnum clipNodeEnumIn, const QString& urlIn, float fpsIn, bool loopIn, float firstFrameIn, float lastFrameIn) :
clipNodeEnum(clipNodeEnumIn), url(urlIn), fps(fpsIn), loop(loopIn), firstFrame(firstFrameIn), lastFrame(lastFrameIn) {}
ClipNodeEnum clipNodeEnum;
QString url;
float fps;
bool loop;
float firstFrame;
float lastFrame;
};
struct UserAnimState {
enum ClipNodeEnum {
@ -349,6 +375,7 @@ protected:
};
UserAnimState _userAnimState;
NetworkAnimState _networkAnimState;
std::map<QString, RoleAnimState> _roleAnimStates;
float _leftHandOverlayAlpha { 0.0f };
@ -382,6 +409,7 @@ protected:
int _rigId;
bool _headEnabled { false };
bool _sendNetworkNode { false };
AnimContext _lastContext;
AnimVariantMap _lastAnimVars;

View file

@ -114,27 +114,29 @@ void Avatar::setShowNamesAboveHeads(bool show) {
}
AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config) {
glm::vec3 currentPosition = _isTransiting ? _currentPosition : avatarPosition;
float oneFrameDistance = glm::length(currentPosition - _lastPosition);
const float MAX_TRANSIT_DISTANCE = 30.0f;
float scaledMaxTransitDistance = MAX_TRANSIT_DISTANCE * _scale;
if (oneFrameDistance > config._triggerDistance && !_isTransiting) {
if (oneFrameDistance < scaledMaxTransitDistance) {
start(deltaTime, _lastPosition, currentPosition, config);
float oneFrameDistance = _isActive ? glm::length(avatarPosition - _endPosition) : glm::length(avatarPosition - _lastPosition);
if (oneFrameDistance > (config._minTriggerDistance * _scale)) {
if (oneFrameDistance < (config._maxTriggerDistance * _scale)) {
start(deltaTime, _lastPosition, avatarPosition, config);
} else {
_lastPosition = currentPosition;
return Status::ABORT_TRANSIT;
_lastPosition = avatarPosition;
_status = Status::ABORT_TRANSIT;
}
}
_lastPosition = currentPosition;
_lastPosition = avatarPosition;
_status = updatePosition(deltaTime);
if (_isActive && oneFrameDistance > (config._abortDistance * _scale) && _status == Status::POST_TRANSIT) {
reset();
_status = Status::ENDED;
}
return _status;
}
void AvatarTransit::reset() {
_lastPosition = _endPosition;
_currentPosition = _endPosition;
_isTransiting = false;
_isActive = false;
}
void AvatarTransit::start(float deltaTime, const glm::vec3& startPosition, const glm::vec3& endPosition, const AvatarTransit::TransitConfig& config) {
_startPosition = startPosition;
@ -143,12 +145,14 @@ void AvatarTransit::start(float deltaTime, const glm::vec3& startPosition, const
_transitLine = endPosition - startPosition;
_totalDistance = glm::length(_transitLine);
_easeType = config._easeType;
const float REFERENCE_FRAMES_PER_SECOND = 30.0f;
_preTransitTime = AVATAR_PRE_TRANSIT_FRAME_COUNT / AVATAR_TRANSIT_FRAMES_PER_SECOND;
_postTransitTime = AVATAR_POST_TRANSIT_FRAME_COUNT / AVATAR_TRANSIT_FRAMES_PER_SECOND;
int transitFrames = (!config._isDistanceBased) ? config._totalFrames : config._framesPerMeter * _totalDistance;
_totalTime = (float)transitFrames / REFERENCE_FRAMES_PER_SECOND;
_currentTime = 0.0f;
_isTransiting = true;
_transitTime = (float)transitFrames / AVATAR_TRANSIT_FRAMES_PER_SECOND;
_totalTime = _transitTime + _preTransitTime + _postTransitTime;
_currentTime = _isActive ? _preTransitTime : 0.0f;
_isActive = true;
}
float AvatarTransit::getEaseValue(AvatarTransit::EaseType type, float value) {
@ -171,31 +175,37 @@ float AvatarTransit::getEaseValue(AvatarTransit::EaseType type, float value) {
AvatarTransit::Status AvatarTransit::updatePosition(float deltaTime) {
Status status = Status::IDLE;
if (_isTransiting) {
if (_isActive) {
float nextTime = _currentTime + deltaTime;
if (nextTime >= _totalTime) {
_currentPosition = _endPosition;
_isTransiting = false;
status = Status::END_TRANSIT;
} else {
if (nextTime < _preTransitTime) {
_currentPosition = _startPosition;
status = Status::PRE_TRANSIT;
if (_currentTime == 0) {
status = Status::STARTED;
}
} else if (nextTime < _totalTime - _postTransitTime){
status = Status::TRANSITING;
if (_currentTime <= _preTransitTime) {
status = Status::START_TRANSIT;
} else {
status = Status::TRANSITING;
float percentageIntoTransit = (nextTime - _preTransitTime) / _transitTime;
_currentPosition = _startPosition + getEaseValue(_easeType, percentageIntoTransit) * _transitLine;
}
} else {
status = Status::POST_TRANSIT;
_currentPosition = _endPosition;
if (nextTime >= _totalTime) {
_isActive = false;
status = Status::ENDED;
} else if (_currentTime < _totalTime - _postTransitTime) {
status = Status::END_TRANSIT;
}
float percentageIntoTransit = nextTime / _totalTime;
_currentPosition = _startPosition + getEaseValue(_easeType, percentageIntoTransit) * _transitLine;
}
_currentTime = nextTime;
}
return status;
}
bool AvatarTransit::getNextPosition(glm::vec3& nextPosition) {
nextPosition = _currentPosition;
return _isTransiting;
}
Avatar::Avatar(QThread* thread) :
_voiceSphereID(GeometryCache::UNKNOWN_ID)
{
@ -536,16 +546,10 @@ void Avatar::relayJointDataToChildren() {
void Avatar::simulate(float deltaTime, bool inView) {
PROFILE_RANGE(simulation, "simulate");
if (_transit.isTransiting()) {
glm::vec3 nextPosition;
if (_transit.getNextPosition(nextPosition)) {
_globalPosition = nextPosition;
_globalPositionChanged = usecTimestampNow();
if (!hasParent()) {
setLocalPosition(nextPosition);
}
}
_globalPosition = _transit.isActive() ? _transit.getCurrentPosition() : _serverPosition;
if (!hasParent()) {
setLocalPosition(_globalPosition);
}
_simulationRate.increment();
@ -558,7 +562,7 @@ void Avatar::simulate(float deltaTime, bool inView) {
PROFILE_RANGE(simulation, "updateJoints");
if (inView) {
Head* head = getHead();
if (_hasNewJointData || _transit.isTransiting()) {
if (_hasNewJointData || _transit.isActive()) {
_skeletonModel->getRig().copyJointsFromJointData(_jointData);
glm::mat4 rootTransform = glm::scale(_skeletonModel->getScale()) * glm::translate(_skeletonModel->getOffset());
_skeletonModel->getRig().computeExternalPoses(rootTransform);
@ -1995,22 +1999,12 @@ float Avatar::getUnscaledEyeHeightFromSkeleton() const {
}
}
AvatarTransit::Status Avatar::updateTransit(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config) {
AvatarTransit::Status Avatar::updateTransit(float deltaTime, const glm::vec3& avatarPosition, float avatarScale, const AvatarTransit::TransitConfig& config) {
std::lock_guard<std::mutex> lock(_transitLock);
_transit.setScale(avatarScale);
return _transit.update(deltaTime, avatarPosition, config);
}
void Avatar::setTransitScale(float scale) {
std::lock_guard<std::mutex> lock(_transitLock);
return _transit.setScale(scale);
}
void Avatar::overrideNextPackagePositionData(const glm::vec3& position) {
std::lock_guard<std::mutex> lock(_transitLock);
_overrideGlobalPosition = true;
_globalPositionOverride = position;
}
void Avatar::addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) {
std::lock_guard<std::mutex> lock(_materialsLock);
_materials[parentMaterialName].push(material);

View file

@ -56,9 +56,13 @@ class AvatarTransit {
public:
enum Status {
IDLE = 0,
STARTED,
PRE_TRANSIT,
START_TRANSIT,
TRANSITING,
END_TRANSIT,
POST_TRANSIT,
ENDED,
ABORT_TRANSIT
};
@ -72,20 +76,20 @@ public:
struct TransitConfig {
TransitConfig() {};
int _totalFrames { 0 };
int _framesPerMeter { 0 };
float _framesPerMeter { 0.0f };
bool _isDistanceBased { false };
float _triggerDistance { 0 };
float _minTriggerDistance { 0.0f };
float _maxTriggerDistance { 0.0f };
float _abortDistance{ 0.0f };
EaseType _easeType { EaseType::EASE_OUT };
};
AvatarTransit() {};
Status update(float deltaTime, const glm::vec3& avatarPosition, const TransitConfig& config);
Status getStatus() { return _status; }
bool isTransiting() { return _isTransiting; }
bool isActive() { return _isActive; }
glm::vec3 getCurrentPosition() { return _currentPosition; }
bool getNextPosition(glm::vec3& nextPosition);
glm::vec3 getEndPosition() { return _endPosition; }
float getTransitTime() { return _totalTime; }
void setScale(float scale) { _scale = scale; }
void reset();
@ -93,7 +97,7 @@ private:
Status updatePosition(float deltaTime);
void start(float deltaTime, const glm::vec3& startPosition, const glm::vec3& endPosition, const TransitConfig& config);
float getEaseValue(AvatarTransit::EaseType type, float value);
bool _isTransiting { false };
bool _isActive { false };
glm::vec3 _startPosition;
glm::vec3 _endPosition;
@ -103,7 +107,10 @@ private:
glm::vec3 _transitLine;
float _totalDistance { 0.0f };
float _preTransitTime { 0.0f };
float _totalTime { 0.0f };
float _transitTime { 0.0f };
float _postTransitTime { 0.0f };
float _currentTime { 0.0f };
EaseType _easeType { EaseType::EASE_OUT };
Status _status { Status::IDLE };
@ -431,11 +438,7 @@ public:
virtual scriptable::ScriptableModelBase getScriptableModel() override;
std::shared_ptr<AvatarTransit> getTransit() { return std::make_shared<AvatarTransit>(_transit); };
AvatarTransit::Status updateTransit(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config);
void setTransitScale(float scale);
void overrideNextPackagePositionData(const glm::vec3& position);
AvatarTransit::Status updateTransit(float deltaTime, const glm::vec3& avatarPosition, float avatarScale, const AvatarTransit::TransitConfig& config);
signals:
void targetScaleChanged(float targetScale);

View file

@ -375,12 +375,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
if (hasAvatarGlobalPosition) {
auto startSection = destinationBuffer;
if (_overrideGlobalPosition) {
AVATAR_MEMCPY(_globalPositionOverride);
} else {
AVATAR_MEMCPY(_globalPosition);
}
AVATAR_MEMCPY(_globalPosition);
int numBytes = destinationBuffer - startSection;
@ -887,20 +882,22 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
offset = glm::vec3(row * SPACE_BETWEEN_AVATARS, 0.0f, col * SPACE_BETWEEN_AVATARS);
}
auto newValue = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]) + offset;
if (_globalPosition != newValue) {
_globalPosition = newValue;
_serverPosition = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]) + offset;
auto oneStepDistance = glm::length(_globalPosition - _serverPosition);
if (oneStepDistance <= AVATAR_TRANSIT_MIN_TRIGGER_DISTANCE || oneStepDistance >= AVATAR_TRANSIT_MAX_TRIGGER_DISTANCE) {
_globalPosition = _serverPosition;
// if we don't have a parent, make sure to also set our local position
if (!hasParent()) {
setLocalPosition(_serverPosition);
}
}
if (_globalPosition != _serverPosition) {
_globalPositionChanged = now;
}
sourceBuffer += sizeof(AvatarDataPacket::AvatarGlobalPosition);
int numBytesRead = sourceBuffer - startSection;
_globalPositionRate.increment(numBytesRead);
_globalPositionUpdateRate.increment();
// if we don't have a parent, make sure to also set our local position
if (!hasParent()) {
setLocalPosition(newValue);
}
}
if (hasAvatarBoundingBox) {
@ -2115,10 +2112,6 @@ void AvatarData::sendAvatarDataPacket(bool sendAll) {
}
}
if (_overrideGlobalPosition) {
_overrideGlobalPosition = false;
}
doneEncoding(cullSmallData);
static AvatarDataSequenceNumber sequenceNumber = 0;

View file

@ -327,6 +327,17 @@ const float AVATAR_DISTANCE_LEVEL_5 = 200.0f; // meters
// 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 = 11;
const float AVATAR_TRANSIT_FRAMES_PER_METER = 0.5f;
const float AVATAR_TRANSIT_ABORT_DISTANCE = 0.1f;
const bool AVATAR_TRANSIT_DISTANCE_BASED = true;
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,
@ -1378,8 +1389,7 @@ protected:
// 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 _globalPositionOverride { 0, 0, 0 };
bool _overrideGlobalPosition { false };
glm::vec3 _serverPosition { 0, 0, 0 };
quint64 _globalPositionChanged { 0 };
quint64 _avatarBoundingBoxChanged { 0 };