mirror of
https://github.com/overte-org/overte.git
synced 2025-04-22 13:53:26 +02:00
Blend network animation
This commit is contained in:
parent
078baa86e4
commit
bb5c042f16
10 changed files with 241 additions and 144 deletions
interface
libraries
scripts/system/controllers/controllerModules
140
interface/resources/avatar/network-animation.json
Normal file
140
interface/resources/avatar/network-animation.json
Normal 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": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -83,18 +83,10 @@ AvatarManager::AvatarManager(QObject* parent) :
|
|||
const int AVATAR_TRANSIT_FRAME_COUNT = 11; // Based on testing
|
||||
const float AVATAR_TRANSIT_FRAMES_PER_METER = 0.5f; // Based on testing
|
||||
|
||||
const QString START_ANIMATION_URL = "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx";
|
||||
const QString MIDDLE_ANIMATION_URL = "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx";
|
||||
const QString END_ANIMATION_URL = "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx";
|
||||
|
||||
_transitConfig._totalFrames = AVATAR_TRANSIT_FRAME_COUNT;
|
||||
_transitConfig._triggerDistance = AVATAR_TRANSIT_TRIGGER_DISTANCE;
|
||||
_transitConfig._framesPerMeter = AVATAR_TRANSIT_FRAMES_PER_METER;
|
||||
_transitConfig._isDistanceBased = true;
|
||||
|
||||
_transitConfig._startTransitAnimation = AvatarTransit::TransitAnimation(START_ANIMATION_URL, 0, 10);
|
||||
_transitConfig._middleTransitAnimation = AvatarTransit::TransitAnimation(MIDDLE_ANIMATION_URL, 15, 0);
|
||||
_transitConfig._endTransitAnimation = AvatarTransit::TransitAnimation(END_ANIMATION_URL, 22, 27);
|
||||
}
|
||||
|
||||
AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) {
|
||||
|
@ -143,30 +135,22 @@ void AvatarManager::setSpace(workload::SpacePointer& space ) {
|
|||
}
|
||||
|
||||
void AvatarManager::handleTransitAnimations(AvatarTransit::Status status) {
|
||||
auto startAnimation = _transitConfig._startTransitAnimation;
|
||||
auto middleAnimation = _transitConfig._middleTransitAnimation;
|
||||
auto endAnimation = _transitConfig._endTransitAnimation;
|
||||
|
||||
const float REFERENCE_FPS = 30.0f;
|
||||
switch (status) {
|
||||
case AvatarTransit::Status::STARTED:
|
||||
qDebug() << "START_FRAME";
|
||||
_myAvatar->overrideNetworkAnimation(startAnimation._animationUrl, REFERENCE_FPS, false, startAnimation._firstFrame,
|
||||
startAnimation._firstFrame + startAnimation._frameCount);
|
||||
_myAvatar->getSkeletonModel()->getRig().triggerNetworkAnimation("preTransitAnim");
|
||||
break;
|
||||
case AvatarTransit::Status::START_TRANSIT:
|
||||
qDebug() << "START_TRANSIT";
|
||||
_myAvatar->overrideNetworkAnimation(middleAnimation._animationUrl, REFERENCE_FPS, false, middleAnimation._firstFrame,
|
||||
middleAnimation._firstFrame + middleAnimation._frameCount);
|
||||
_myAvatar->getSkeletonModel()->getRig().triggerNetworkAnimation("transitAnim");
|
||||
break;
|
||||
case AvatarTransit::Status::END_TRANSIT:
|
||||
qDebug() << "END_TRANSIT";
|
||||
_myAvatar->overrideNetworkAnimation(endAnimation._animationUrl, REFERENCE_FPS, false, endAnimation._firstFrame,
|
||||
endAnimation._firstFrame + endAnimation._frameCount);
|
||||
_myAvatar->getSkeletonModel()->getRig().triggerNetworkAnimation("postTransitAnim");
|
||||
break;
|
||||
case AvatarTransit::Status::ENDED:
|
||||
qDebug() << "END_FRAME";
|
||||
_myAvatar->restoreNetworkAnimation();
|
||||
_myAvatar->getSkeletonModel()->getRig().triggerNetworkAnimation("idleAnim");
|
||||
break;
|
||||
case AvatarTransit::Status::PRE_TRANSIT:
|
||||
break;
|
||||
|
@ -188,9 +172,6 @@ void AvatarManager::updateMyAvatar(float deltaTime) {
|
|||
AvatarTransit::Status status = _myAvatar->updateTransit(deltaTime, _myAvatar->getNextPosition(), _transitConfig);
|
||||
handleTransitAnimations(status);
|
||||
|
||||
bool sendFirstTransitPacket = (status == AvatarTransit::Status::STARTED);
|
||||
bool sendCurrentTransitPacket = (status != AvatarTransit::Status::IDLE && (status != AvatarTransit::Status::POST_TRANSIT || status != AvatarTransit::Status::ENDED));
|
||||
|
||||
_myAvatar->update(deltaTime);
|
||||
render::Transaction transaction;
|
||||
_myAvatar->updateRenderItem(transaction);
|
||||
|
@ -199,19 +180,13 @@ void AvatarManager::updateMyAvatar(float deltaTime) {
|
|||
quint64 now = usecTimestampNow();
|
||||
quint64 dt = now - _lastSendAvatarDataTime;
|
||||
|
||||
if (sendFirstTransitPacket || (dt > MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS && !_myAvatarDataPacketsPaused)) {
|
||||
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 (sendFirstTransitPacket) {
|
||||
_myAvatar->overrideNextPacketPositionData(_myAvatar->getTransit()->getEndPosition());
|
||||
} else if (sendCurrentTransitPacket) {
|
||||
_myAvatar->overrideNextPacketPositionData(_myAvatar->getTransit()->getCurrentPosition());
|
||||
}
|
||||
PerformanceTimer perfTimer("send");
|
||||
_myAvatar->sendAvatarDataPacket();
|
||||
_lastSendAvatarDataTime = now;
|
||||
_myAvatarSendRate.increment();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1124,15 +1124,6 @@ void MyAvatar::overrideAnimation(const QString& url, float fps, bool loop, float
|
|||
_skeletonModel->getRig().overrideAnimation(url, fps, loop, firstFrame, lastFrame);
|
||||
}
|
||||
|
||||
void MyAvatar::overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "overrideNetworkAnimation", Q_ARG(const QString&, url), Q_ARG(float, fps),
|
||||
Q_ARG(bool, loop), Q_ARG(float, firstFrame), Q_ARG(float, lastFrame));
|
||||
return;
|
||||
}
|
||||
_skeletonModel->getRig().overrideNetworkAnimation(url, fps, loop, firstFrame, lastFrame);
|
||||
}
|
||||
|
||||
void MyAvatar::restoreAnimation() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "restoreAnimation");
|
||||
|
@ -1141,14 +1132,6 @@ void MyAvatar::restoreAnimation() {
|
|||
_skeletonModel->getRig().restoreAnimation();
|
||||
}
|
||||
|
||||
void MyAvatar::restoreNetworkAnimation() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "restoreNetworkAnimation");
|
||||
return;
|
||||
}
|
||||
_skeletonModel->getRig().restoreNetworkAnimation();
|
||||
}
|
||||
|
||||
QStringList MyAvatar::getAnimationRoles() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QStringList result;
|
||||
|
|
|
@ -376,7 +376,6 @@ public:
|
|||
* }, 3000);
|
||||
*/
|
||||
Q_INVOKABLE void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
|
||||
Q_INVOKABLE void overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
|
||||
|
||||
/**jsdoc
|
||||
* The avatar animation system includes a set of default animations along with rules for how those animations are blended together with
|
||||
|
@ -393,7 +392,6 @@ public:
|
|||
* }, 3000);
|
||||
*/
|
||||
Q_INVOKABLE void restoreAnimation();
|
||||
Q_INVOKABLE void restoreNetworkAnimation();
|
||||
|
||||
/**jsdoc
|
||||
* Each avatar has an avatar-animation.json file that defines which animations are used and how they are blended together with procedural data
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "AnimSkeleton.h"
|
||||
#include "AnimUtil.h"
|
||||
#include "IKTarget.h"
|
||||
#include "PathUtils.h"
|
||||
|
||||
|
||||
static int nextRigId = 1;
|
||||
|
@ -133,41 +134,28 @@ void Rig::overrideAnimation(const QString& url, float fps, bool loop, float firs
|
|||
_animVars.set("userAnimB", clipNodeEnum == UserAnimState::B);
|
||||
}
|
||||
|
||||
void Rig::overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) {
|
||||
UserAnimState::ClipNodeEnum clipNodeEnum;
|
||||
if (_networkAnimState.clipNodeEnum == UserAnimState::None || _networkAnimState.clipNodeEnum == UserAnimState::B) {
|
||||
clipNodeEnum = UserAnimState::A;
|
||||
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;
|
||||
}
|
||||
else {
|
||||
clipNodeEnum = UserAnimState::B;
|
||||
}
|
||||
if (_networkNode) {
|
||||
// find an unused AnimClip clipNode
|
||||
_sendNetworkNode = true;
|
||||
std::shared_ptr<AnimClip> clip;
|
||||
if (clipNodeEnum == UserAnimState::A) {
|
||||
clip = std::dynamic_pointer_cast<AnimClip>(_networkNode->findByName("userAnimA"));
|
||||
}
|
||||
else {
|
||||
clip = std::dynamic_pointer_cast<AnimClip>(_networkNode->findByName("userAnimB"));
|
||||
}
|
||||
if (clip) {
|
||||
// set parameters
|
||||
clip->setLoopFlag(loop);
|
||||
clip->setStartFrame(firstFrame);
|
||||
clip->setEndFrame(lastFrame);
|
||||
const float REFERENCE_FRAMES_PER_SECOND = 30.0f;
|
||||
float timeScale = fps / REFERENCE_FRAMES_PER_SECOND;
|
||||
clip->setTimeScale(timeScale);
|
||||
clip->loadURL(url);
|
||||
}
|
||||
}
|
||||
// store current user anim state.
|
||||
_networkAnimState = { clipNodeEnum, url, fps, loop, firstFrame, lastFrame };
|
||||
// notify the userAnimStateMachine the desired state.
|
||||
_networkVars.set("userAnimNone", false);
|
||||
_networkVars.set("userAnimA", clipNodeEnum == UserAnimState::A);
|
||||
_networkVars.set("userAnimB", clipNodeEnum == UserAnimState::B);
|
||||
|
||||
}
|
||||
|
||||
void Rig::restoreAnimation() {
|
||||
|
@ -182,13 +170,12 @@ void Rig::restoreAnimation() {
|
|||
}
|
||||
|
||||
void Rig::restoreNetworkAnimation() {
|
||||
_sendNetworkNode = false;
|
||||
if (_networkAnimState.clipNodeEnum != UserAnimState::None) {
|
||||
_networkAnimState.clipNodeEnum = UserAnimState::None;
|
||||
// notify the userAnimStateMachine the desired state.
|
||||
_networkVars.set("userAnimNone", true);
|
||||
_networkVars.set("userAnimA", false);
|
||||
_networkVars.set("userAnimB", false);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1137,6 +1124,24 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons
|
|||
_internalPoseSet._relativePoses = _animNode->evaluate(_animVars, context, deltaTime, triggersOut);
|
||||
if (_networkNode) {
|
||||
_networkPoseSet._relativePoses = _networkNode->evaluate(_networkVars, context, deltaTime, networkTriggersOut);
|
||||
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()) / 6.0f;
|
||||
}
|
||||
} else if (_networkAnimState.clipNodeEnum == NetworkAnimState::PostTransit) {
|
||||
clip = std::dynamic_pointer_cast<AnimClip>(_networkNode->findByName("postTransitAnim"));
|
||||
if (clip) {
|
||||
alpha = (clip->getEndFrame() - clip->getFrame()) / 6.0f;
|
||||
}
|
||||
}
|
||||
if (_sendNetworkNode) {
|
||||
for (int 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.
|
||||
|
@ -1798,10 +1803,10 @@ void Rig::initAnimGraph(const QUrl& url) {
|
|||
|
||||
// load the anim graph
|
||||
_animLoader.reset(new AnimNodeLoader(url));
|
||||
_networkLoader.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.
|
||||
|
@ -1828,10 +1833,10 @@ 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](AnimNode::Pointer nodeIn) {
|
||||
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();
|
||||
|
@ -1839,16 +1844,22 @@ void Rig::initAnimGraph(const QUrl& url) {
|
|||
return;
|
||||
}
|
||||
_networkNode->setSkeleton(sharedSkeletonPtr);
|
||||
if (_networkAnimState.clipNodeEnum != UserAnimState::None) {
|
||||
if (_networkAnimState.clipNodeEnum != NetworkAnimState::Idle) {
|
||||
// restore the user animation we had before reset.
|
||||
UserAnimState origState = _networkAnimState;
|
||||
_networkAnimState = { UserAnimState::None, "", 30.0f, false, 0.0f, 0.0f };
|
||||
overrideNetworkAnimation(origState.url, origState.fps, origState.loop, origState.firstFrame, origState.lastFrame);
|
||||
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");
|
||||
}
|
||||
}
|
||||
// emit onLoadComplete();
|
||||
|
||||
});
|
||||
connect(_networkLoader.get(), &AnimNodeLoader::error, [url](int error, QString str) {
|
||||
qCCritical(animation) << "Error loading" << url.toDisplayString() << "code = " << error << "str =" << str;
|
||||
connect(_networkLoader.get(), &AnimNodeLoader::error, [networkUrl](int error, QString str) {
|
||||
qCritical(animation) << "Error loading" << networkUrl.toDisplayString() << "code = " << error << "str =" << str;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,7 +113,7 @@ public:
|
|||
void destroyAnimGraph();
|
||||
|
||||
void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
|
||||
void overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
|
||||
void triggerNetworkAnimation(const QString& animName);
|
||||
void restoreAnimation();
|
||||
void restoreNetworkAnimation();
|
||||
|
||||
|
@ -323,6 +323,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 {
|
||||
|
@ -357,7 +376,7 @@ protected:
|
|||
};
|
||||
|
||||
UserAnimState _userAnimState;
|
||||
UserAnimState _networkAnimState;
|
||||
NetworkAnimState _networkAnimState;
|
||||
std::map<QString, RoleAnimState> _roleAnimStates;
|
||||
|
||||
float _leftHandOverlayAlpha { 0.0f };
|
||||
|
|
|
@ -114,12 +114,10 @@ void Avatar::setShowNamesAboveHeads(bool show) {
|
|||
}
|
||||
|
||||
AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config) {
|
||||
bool checkDistance = (!_isActive || (_status == Status::POST_TRANSIT || _status == Status::ENDED));
|
||||
float oneFrameDistance = _isActive ? glm::length(avatarPosition - _endPosition) : glm::length(avatarPosition - _lastPosition);
|
||||
|
||||
const float TRANSIT_TRIGGER_MAX_DISTANCE = 30.0f;
|
||||
float scaledMaxTransitDistance = TRANSIT_TRIGGER_MAX_DISTANCE * _scale;
|
||||
if (oneFrameDistance > config._triggerDistance && checkDistance) {
|
||||
if (oneFrameDistance > config._triggerDistance) {
|
||||
if (oneFrameDistance < scaledMaxTransitDistance) {
|
||||
start(deltaTime, _lastPosition, avatarPosition, config);
|
||||
} else {
|
||||
|
@ -132,12 +130,9 @@ AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& av
|
|||
|
||||
const float SETTLE_ABORT_DISTANCE = 0.1f;
|
||||
float scaledSettleAbortDistance = SETTLE_ABORT_DISTANCE * _scale;
|
||||
bool abortPostTransit = (_status == Status::POST_TRANSIT && _purpose == Purpose::SIT) ||
|
||||
(_isActive && oneFrameDistance > scaledSettleAbortDistance && _status == Status::POST_TRANSIT);
|
||||
if (abortPostTransit) {
|
||||
if (_isActive && oneFrameDistance > scaledSettleAbortDistance && _status == Status::POST_TRANSIT) {
|
||||
reset();
|
||||
_status = Status::ENDED;
|
||||
_purpose = Purpose::UNDEFINED;
|
||||
}
|
||||
return _status;
|
||||
}
|
||||
|
@ -154,10 +149,13 @@ 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 = (float)config._startTransitAnimation._frameCount / REFERENCE_FRAMES_PER_SECOND;
|
||||
_postTransitTime = (float)config._endTransitAnimation._frameCount / REFERENCE_FRAMES_PER_SECOND;
|
||||
|
||||
const float REFERENCE_FRAMES_PER_SECOND = 30.0f;
|
||||
const float PRE_TRANSIT_FRAME_COUNT = 10.0f;
|
||||
const float POST_TRANSIT_FRAME_COUNT = 27.0f;
|
||||
|
||||
_preTransitTime = PRE_TRANSIT_FRAME_COUNT / REFERENCE_FRAMES_PER_SECOND;
|
||||
_postTransitTime = POST_TRANSIT_FRAME_COUNT / REFERENCE_FRAMES_PER_SECOND;
|
||||
int transitFrames = (!config._isDistanceBased) ? config._totalFrames : config._framesPerMeter * _totalDistance;
|
||||
_transitTime = (float)transitFrames / REFERENCE_FRAMES_PER_SECOND;
|
||||
_totalTime = _transitTime + _preTransitTime + _postTransitTime;
|
||||
|
@ -206,7 +204,6 @@ AvatarTransit::Status AvatarTransit::updatePosition(float deltaTime) {
|
|||
_currentPosition = _endPosition;
|
||||
if (nextTime >= _totalTime) {
|
||||
_isActive = false;
|
||||
_purpose = Purpose::UNDEFINED;
|
||||
status = Status::ENDED;
|
||||
} else if (_currentTime < _totalTime - _postTransitTime) {
|
||||
status = Status::END_TRANSIT;
|
||||
|
@ -2007,11 +2004,6 @@ void Avatar::setTransitScale(float scale) {
|
|||
_transit.setScale(scale);
|
||||
}
|
||||
|
||||
void Avatar::setTransitPurpose(int purpose) {
|
||||
std::lock_guard<std::mutex> lock(_transitLock);
|
||||
_transit.setPurpose(static_cast<AvatarTransit::Purpose>(purpose));
|
||||
}
|
||||
|
||||
void Avatar::overrideNextPacketPositionData(const glm::vec3& position) {
|
||||
std::lock_guard<std::mutex> lock(_transitLock);
|
||||
_overrideGlobalPosition = true;
|
||||
|
|
|
@ -71,21 +71,6 @@ public:
|
|||
EASE_IN_OUT
|
||||
};
|
||||
|
||||
enum Purpose {
|
||||
UNDEFINED = 0,
|
||||
TELEPORT,
|
||||
SIT
|
||||
};
|
||||
|
||||
struct TransitAnimation {
|
||||
TransitAnimation(){};
|
||||
TransitAnimation(const QString& animationUrl, int firstFrame, int frameCount) :
|
||||
_firstFrame(firstFrame), _frameCount(frameCount), _animationUrl(animationUrl){};
|
||||
int _firstFrame;
|
||||
int _frameCount;
|
||||
QString _animationUrl;
|
||||
};
|
||||
|
||||
struct TransitConfig {
|
||||
TransitConfig() {};
|
||||
int _totalFrames { 0 };
|
||||
|
@ -93,9 +78,6 @@ public:
|
|||
bool _isDistanceBased { false };
|
||||
float _triggerDistance { 0 };
|
||||
EaseType _easeType { EaseType::EASE_OUT };
|
||||
TransitAnimation _startTransitAnimation;
|
||||
TransitAnimation _middleTransitAnimation;
|
||||
TransitAnimation _endTransitAnimation;
|
||||
};
|
||||
|
||||
AvatarTransit() {};
|
||||
|
@ -105,7 +87,6 @@ public:
|
|||
glm::vec3 getCurrentPosition() { return _currentPosition; }
|
||||
glm::vec3 getEndPosition() { return _endPosition; }
|
||||
void setScale(float scale) { _scale = scale; }
|
||||
void setPurpose(const Purpose& purpose) { _purpose = purpose; }
|
||||
void reset();
|
||||
|
||||
private:
|
||||
|
@ -130,7 +111,6 @@ private:
|
|||
EaseType _easeType { EaseType::EASE_OUT };
|
||||
Status _status { Status::IDLE };
|
||||
float _scale { 1.0f };
|
||||
Purpose _purpose { Purpose::UNDEFINED };
|
||||
};
|
||||
|
||||
class Avatar : public AvatarData, public scriptable::ModelProvider {
|
||||
|
@ -457,7 +437,6 @@ public:
|
|||
|
||||
AvatarTransit::Status updateTransit(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config);
|
||||
void setTransitScale(float scale);
|
||||
Q_INVOKABLE void setTransitPurpose(int purpose);
|
||||
|
||||
void overrideNextPacketPositionData(const glm::vec3& position);
|
||||
|
||||
|
|
|
@ -781,13 +781,11 @@ Script.include("/~/system/libraries/controllers.js");
|
|||
if (target === TARGET.NONE || target === TARGET.INVALID) {
|
||||
// Do nothing
|
||||
} else if (target === TARGET.SEAT) {
|
||||
MyAvatar.setTransitPurpose(2);
|
||||
Entities.callEntityMethod(result.objectID, 'sit');
|
||||
} else if (target === TARGET.SURFACE || target === TARGET.DISCREPANCY) {
|
||||
var offset = getAvatarFootOffset();
|
||||
result.intersection.y += offset;
|
||||
var shouldLandSafe = target === TARGET.DISCREPANCY;
|
||||
MyAvatar.setTransitPurpose(1);
|
||||
MyAvatar.goToLocation(result.intersection, true, HMD.orientation, false, shouldLandSafe);
|
||||
HMD.centerUI();
|
||||
MyAvatar.centerBody();
|
||||
|
|
Loading…
Reference in a new issue