Merge pull request #7649 from hyperlogic/tony/simplify-away-mode

away.js: fix for permanent disabling avatar IK
This commit is contained in:
Howard Stearns 2016-04-15 13:35:14 -07:00
commit e4f8a67e60
4 changed files with 82 additions and 145 deletions

View file

@ -35,49 +35,26 @@ var OVERLAY_DATA_HMD = {
alpha: 1,
scale: 2,
isFacingAvatar: true,
drawInFront: true
drawInFront: true
};
// ANIMATION
// We currently don't have play/stopAnimation integrated with the animation graph, but we can get the same effect
// using an animation graph with a state that we turn on and off through the animation var defined with that state.
var awayAnimationHandlerId, activeAnimationHandlerId, stopper;
var AWAY_INTRO = {
url: "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/kneel.fbx",
playbackRate: 30.0,
loopFlag: false,
startFrame: 0.0,
endFrame: 83.0
};
// prefetch the kneel animation so it's resident in memory when we need it.
MyAvatar.prefetchAnimation(AWAY_INTRO.url);
function playAwayAnimation() {
function animateAway() {
return {isAway: true, isNotAway: false, isNotMoving: false, ikOverlayAlpha: 0.0};
}
if (stopper) {
stopper = false;
MyAvatar.removeAnimationStateHandler(activeAnimationHandlerId); // do it now, before making new assignment
}
awayAnimationHandlerId = MyAvatar.addAnimationStateHandler(animateAway, null);
MyAvatar.overrideAnimation(AWAY_INTRO.url, AWAY_INTRO.playbackRate, AWAY_INTRO.loopFlag, AWAY_INTRO.startFrame, AWAY_INTRO.endFrame);
}
function stopAwayAnimation() {
MyAvatar.removeAnimationStateHandler(awayAnimationHandlerId);
if (stopper) {
print('WARNING: unexpected double stop');
return;
}
// How do we know when to turn ikOverlayAlpha back on?
// It cannot be as soon as we want to stop the away animation, because then things will look goofy as we come out of that animation.
// (Imagine an away animation that sits or kneels, and then stands back up when coming out of it. If head is at the HMD, then it won't
// want to track the standing up animation.)
// The anim graph will trigger awayOutroOnDone when awayOutro is finished.
var backToNormal = false;
stopper = true;
function animateActive(state) {
if (state.awayOutroOnDone) {
backToNormal = true;
stopper = false;
} else if (state.ikOverlayAlpha) {
// Once the right state gets reflected back to us, we don't need the hander any more.
// But we are locked against handler changes during the execution of a handler, so remove asynchronously.
Script.setTimeout(function () { MyAvatar.removeAnimationStateHandler(activeAnimationHandlerId); }, 0);
}
// It might be cool to "come back to life" by fading the ik overlay back in over a short time. But let's see how this goes.
return {isAway: false, isNotAway: true, ikOverlayAlpha: backToNormal ? 1.0 : 0.0}; // IWBNI we had a way of deleting an anim var.
}
activeAnimationHandlerId = MyAvatar.addAnimationStateHandler(animateActive, ['ikOverlayAlpha', 'awayOutroOnDone']);
MyAvatar.restoreAnimation();
}
// OVERLAY
@ -113,15 +90,17 @@ function showOverlay() {
var screen = Controller.getViewportDimensions();
// keep the overlay it's natural size and always center it...
Overlays.editOverlay(overlay, { visible: true,
x: ((screen.x - OVERLAY_WIDTH) / 2),
Overlays.editOverlay(overlay, { visible: true,
x: ((screen.x - OVERLAY_WIDTH) / 2),
y: ((screen.y - OVERLAY_HEIGHT) / 2) });
}
}
function hideOverlay() {
Overlays.editOverlay(overlay, {visible: false});
Overlays.editOverlay(overlayHMD, {visible: false});
}
hideOverlay();
function maybeMoveOverlay() {
@ -171,6 +150,7 @@ function safeGetHMDMounted() {
}
return HMD.mounted;
}
var wasHmdMounted = safeGetHMDMounted();
function goAway() {
@ -277,7 +257,7 @@ Script.update.connect(maybeGoAway);
Controller.mousePressEvent.connect(goActive);
Controller.keyPressEvent.connect(maybeGoActive);
// Note peek() so as to not interfere with other mappings.
eventMapping.from(Controller.Standard.LeftPrimaryThumb).peek().to(goActive);
eventMapping.from(Controller.Standard.LeftPrimaryThumb).peek().to(goActive);
eventMapping.from(Controller.Standard.RightPrimaryThumb).peek().to(goActive);
eventMapping.from(Controller.Standard.LeftSecondaryThumb).peek().to(goActive);
eventMapping.from(Controller.Standard.RightSecondaryThumb).peek().to(goActive);

View file

@ -246,7 +246,6 @@
{ "var": "isMovingLeft", "state": "strafeLeft" },
{ "var": "isTurningRight", "state": "turnRight" },
{ "var": "isTurningLeft", "state": "turnLeft" },
{ "var": "isAway", "state": "awayIntro" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
@ -266,7 +265,6 @@
{ "var": "isMovingLeft", "state": "strafeLeft" },
{ "var": "isTurningRight", "state": "turnRight" },
{ "var": "isTurningLeft", "state": "turnLeft" },
{ "var": "isAway", "state": "awayIntro" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
@ -285,7 +283,6 @@
{ "var": "isMovingLeft", "state": "strafeLeft" },
{ "var": "isTurningRight", "state": "turnRight" },
{ "var": "isTurningLeft", "state": "turnLeft" },
{ "var": "isAway", "state": "awayIntro" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
@ -304,7 +301,6 @@
{ "var": "isMovingLeft", "state": "strafeLeft" },
{ "var": "isTurningRight", "state": "turnRight" },
{ "var": "isTurningLeft", "state": "turnLeft" },
{ "var": "isAway", "state": "awayIntro" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
@ -323,7 +319,6 @@
{ "var": "isMovingLeft", "state": "strafeLeft" },
{ "var": "isTurningRight", "state": "turnRight" },
{ "var": "isTurningLeft", "state": "turnLeft" },
{ "var": "isAway", "state": "awayIntro" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
@ -342,7 +337,6 @@
{ "var": "isMovingRight", "state": "strafeRight" },
{ "var": "isTurningRight", "state": "turnRight" },
{ "var": "isTurningLeft", "state": "turnLeft" },
{ "var": "isAway", "state": "awayIntro" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
@ -361,7 +355,6 @@
{ "var": "isMovingRight", "state": "strafeRight" },
{ "var": "isMovingLeft", "state": "strafeLeft" },
{ "var": "isTurningLeft", "state": "turnLeft" },
{ "var": "isAway", "state": "awayIntro" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
@ -380,7 +373,6 @@
{ "var": "isMovingRight", "state": "strafeRight" },
{ "var": "isMovingLeft", "state": "strafeLeft" },
{ "var": "isTurningRight", "state": "turnRight" },
{ "var": "isAway", "state": "awayIntro" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
@ -388,37 +380,11 @@
{ "var": "isInAirRun", "state": "inAirRun" }
]
},
{
"id": "awayIntro",
"interpTarget": 30,
"interpDuration": 30,
"transitions": [
{ "var": "isNotAway", "state": "awayOutro" },
{ "var": "awayIntroOnDone", "state": "away"}
]
},
{
"id": "away",
"interpTarget": 3,
"interpDuration": 3,
"transitions": [
{ "var": "isNotAway", "state": "awayOutro" }
]
},
{
"id": "awayOutro",
"interpTarget": 3,
"interpDuration": 3,
"transitions": [
{ "var": "awayOutroOnDone", "state": "idle" }
]
},
{
"id": "fly",
"interpTarget": 6,
"interpDuration": 6,
"transitions": [
{ "var": "isAway", "state": "awayIntro" },
{ "var": "isNotFlying", "state": "idle" }
]
},
@ -427,7 +393,6 @@
"interpTarget": 0,
"interpDuration": 6,
"transitions": [
{ "var": "isAway", "state": "awayIntro" },
{ "var": "isNotTakeoff", "state": "inAirStand" }
]
},
@ -436,7 +401,6 @@
"interpTarget": 0,
"interpDuration": 6,
"transitions": [
{ "var": "isAway", "state": "awayIntro" },
{ "var": "isNotTakeoff", "state": "inAirRun" }
]
},
@ -446,7 +410,6 @@
"interpDuration": 6,
"interpType": "snapshotPrev",
"transitions": [
{ "var": "isAway", "state": "awayIntro" },
{ "var": "isNotInAir", "state": "landStandImpact" }
]
},
@ -456,7 +419,6 @@
"interpDuration": 6,
"interpType": "snapshotPrev",
"transitions": [
{ "var": "isAway", "state": "awayIntro" },
{ "var": "isNotInAir", "state": "landRun" }
]
},
@ -465,7 +427,6 @@
"interpTarget": 6,
"interpDuration": 4,
"transitions": [
{ "var": "isAway", "state": "awayIntro" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
@ -483,7 +444,6 @@
{ "var": "isMovingLeft", "state": "strafeLeft" },
{ "var": "isTurningRight", "state": "turnRight" },
{ "var": "isTurningLeft", "state": "turnLeft" },
{ "var": "isAway", "state": "awayIntro" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
@ -497,7 +457,6 @@
"interpTarget": 1,
"interpDuration": 7,
"transitions": [
{ "var": "isAway", "state": "awayIntro" },
{ "var": "isFlying", "state": "fly" },
{ "var": "isTakeoffStand", "state": "takeoffStand" },
{ "var": "isTakeoffRun", "state": "takeoffRun" },
@ -754,42 +713,6 @@
}
]
},
{
"id": "awayIntro",
"type": "clip",
"data": {
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/kneel.fbx",
"startFrame": 0.0,
"endFrame": 83.0,
"timeScale": 1.0,
"loopFlag": false
},
"children": []
},
{
"id": "away",
"type": "clip",
"data": {
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/kneel.fbx",
"startFrame": 83.0,
"endFrame": 84.0,
"timeScale": 1.0,
"loopFlag": true
},
"children": []
},
{
"id": "awayOutro",
"type": "clip",
"data": {
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/kneel.fbx",
"startFrame": 84.0,
"endFrame": 167.0,
"timeScale": 1.0,
"loopFlag": false
},
"children": []
},
{
"id": "fly",
"type": "clip",

View file

@ -48,36 +48,47 @@ const glm::vec3 DEFAULT_NECK_POS(0.0f, 0.70f, 0.0f);
void Rig::overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) {
// find an unused AnimClip clipNode
std::shared_ptr<AnimClip> clip;
if (_userAnimState == UserAnimState::None || _userAnimState == UserAnimState::B) {
_userAnimState = UserAnimState::A;
clip = std::dynamic_pointer_cast<AnimClip>(_animNode->findByName("userAnimA"));
} else if (_userAnimState == UserAnimState::A) {
_userAnimState = UserAnimState::B;
clip = std::dynamic_pointer_cast<AnimClip>(_animNode->findByName("userAnimB"));
UserAnimState::ClipNodeEnum clipNodeEnum;
if (_userAnimState.clipNodeEnum == UserAnimState::None || _userAnimState.clipNodeEnum == UserAnimState::B) {
clipNodeEnum = UserAnimState::A;
} else {
clipNodeEnum = UserAnimState::B;
}
// 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);
if (_animNode) {
// find an unused AnimClip clipNode
std::shared_ptr<AnimClip> clip;
if (clipNodeEnum == UserAnimState::A) {
clip = std::dynamic_pointer_cast<AnimClip>(_animNode->findByName("userAnimA"));
} else {
clip = std::dynamic_pointer_cast<AnimClip>(_animNode->findByName("userAnimB"));
}
_currentUserAnimURL = url;
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.
_userAnimState = { clipNodeEnum, url, fps, loop, firstFrame, lastFrame };
// notify the userAnimStateMachine the desired state.
_animVars.set("userAnimNone", false);
_animVars.set("userAnimA", _userAnimState == UserAnimState::A);
_animVars.set("userAnimB", _userAnimState == UserAnimState::B);
_animVars.set("userAnimA", clipNodeEnum == UserAnimState::A);
_animVars.set("userAnimB", clipNodeEnum == UserAnimState::B);
}
void Rig::restoreAnimation() {
if (_currentUserAnimURL != "") {
_currentUserAnimURL = "";
if (_userAnimState.clipNodeEnum != UserAnimState::None) {
_userAnimState.clipNodeEnum = UserAnimState::None;
// notify the userAnimStateMachine the desired state.
_animVars.set("userAnimNone", true);
_animVars.set("userAnimA", false);
@ -1129,6 +1140,14 @@ void Rig::initAnimGraph(const QUrl& url) {
connect(_animLoader.get(), &AnimNodeLoader::success, [this](AnimNode::Pointer nodeIn) {
_animNode = nodeIn;
_animNode->setSkeleton(_animSkeleton);
if (_userAnimState.clipNodeEnum != UserAnimState::None) {
// restore the user animation we had before reset.
UserAnimState origState = _userAnimState;
_userAnimState = { UserAnimState::None, "", 30.0f, false, 0.0f, 0.0f };
overrideAnimation(origState.url, origState.fps, origState.loop, origState.firstFrame, origState.lastFrame);
}
});
connect(_animLoader.get(), &AnimNodeLoader::error, [url](int error, QString str) {
qCCritical(animation) << "Error loading" << url.toDisplayString() << "code = " << error << "str =" << str;

View file

@ -289,13 +289,28 @@ public:
RigRole _state { RigRole::Idle };
RigRole _desiredState { RigRole::Idle };
float _desiredStateAge { 0.0f };
enum class UserAnimState {
None = 0,
A,
B
struct UserAnimState {
enum ClipNodeEnum {
None = 0,
A,
B
};
UserAnimState() : clipNodeEnum(UserAnimState::None) {}
UserAnimState(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;
};
UserAnimState _userAnimState { UserAnimState::None };
QString _currentUserAnimURL;
UserAnimState _userAnimState;
float _leftHandOverlayAlpha { 0.0f };
float _rightHandOverlayAlpha { 0.0f };