Merge pull request #9917 from Atlante45/fix/sit

Cancel Avatar velocity when sitting down.
This commit is contained in:
Andrew Meadows 2017-03-17 10:04:25 -07:00 committed by GitHub
commit 60e0eb0cf9
4 changed files with 180 additions and 95 deletions

View file

@ -4383,16 +4383,16 @@ void Application::update(float deltaTime) {
myAvatar->clearDriveKeys();
if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) {
if (!_controllerScriptingInterface->areActionsCaptured()) {
myAvatar->setDriveKeys(TRANSLATE_Z, -1.0f * userInputMapper->getActionState(controller::Action::TRANSLATE_Z));
myAvatar->setDriveKeys(TRANSLATE_Y, userInputMapper->getActionState(controller::Action::TRANSLATE_Y));
myAvatar->setDriveKeys(TRANSLATE_X, userInputMapper->getActionState(controller::Action::TRANSLATE_X));
myAvatar->setDriveKey(MyAvatar::TRANSLATE_Z, -1.0f * userInputMapper->getActionState(controller::Action::TRANSLATE_Z));
myAvatar->setDriveKey(MyAvatar::TRANSLATE_Y, userInputMapper->getActionState(controller::Action::TRANSLATE_Y));
myAvatar->setDriveKey(MyAvatar::TRANSLATE_X, userInputMapper->getActionState(controller::Action::TRANSLATE_X));
if (deltaTime > FLT_EPSILON) {
myAvatar->setDriveKeys(PITCH, -1.0f * userInputMapper->getActionState(controller::Action::PITCH));
myAvatar->setDriveKeys(YAW, -1.0f * userInputMapper->getActionState(controller::Action::YAW));
myAvatar->setDriveKeys(STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW));
myAvatar->setDriveKey(MyAvatar::PITCH, -1.0f * userInputMapper->getActionState(controller::Action::PITCH));
myAvatar->setDriveKey(MyAvatar::YAW, -1.0f * userInputMapper->getActionState(controller::Action::YAW));
myAvatar->setDriveKey(MyAvatar::STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW));
}
}
myAvatar->setDriveKeys(ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z));
myAvatar->setDriveKey(MyAvatar::ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z));
}
controller::Pose leftHandPose = userInputMapper->getPoseState(controller::Action::LEFT_HAND);
@ -5503,8 +5503,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("Rates", new RatesScriptingInterface(this));
// hook our avatar and avatar hash map object into this script engine
scriptEngine->registerGlobalObject("MyAvatar", getMyAvatar().get());
qScriptRegisterMetaType(scriptEngine, audioListenModeToScriptValue, audioListenModeFromScriptValue);
getMyAvatar()->registerMetaTypes(scriptEngine);
scriptEngine->registerGlobalObject("AvatarList", DependencyManager::get<AvatarManager>().data());

View file

@ -119,9 +119,7 @@ MyAvatar::MyAvatar(RigPointer rig) :
using namespace recording;
_skeletonModel->flagAsCauterized();
for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
_driveKeys[i] = 0.0f;
}
clearDriveKeys();
// Necessary to select the correct slot
using SlotType = void(MyAvatar::*)(const glm::vec3&, bool, const glm::quat&, bool);
@ -230,6 +228,21 @@ MyAvatar::~MyAvatar() {
_lookAtTargetAvatar.reset();
}
void MyAvatar::registerMetaTypes(QScriptEngine* engine) {
QScriptValue value = engine->newQObject(this, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects);
engine->globalObject().setProperty("MyAvatar", value);
QScriptValue driveKeys = engine->newObject();
auto metaEnum = QMetaEnum::fromType<DriveKeys>();
for (int i = 0; i < MAX_DRIVE_KEYS; ++i) {
driveKeys.setProperty(metaEnum.key(i), metaEnum.value(i));
}
engine->globalObject().setProperty("DriveKeys", driveKeys);
qScriptRegisterMetaType(engine, audioListenModeToScriptValue, audioListenModeFromScriptValue);
qScriptRegisterMetaType(engine, driveKeysToScriptValue, driveKeysFromScriptValue);
}
void MyAvatar::setOrientationVar(const QVariant& newOrientationVar) {
Avatar::setOrientation(quatFromVariant(newOrientationVar));
}
@ -462,7 +475,7 @@ void MyAvatar::simulate(float deltaTime) {
// When there are no step values, we zero out the last step pulse.
// This allows a user to do faster snapping by tapping a control
for (int i = STEP_TRANSLATE_X; !stepAction && i <= STEP_YAW; ++i) {
if (_driveKeys[i] != 0.0f) {
if (getDriveKey((DriveKeys)i) != 0.0f) {
stepAction = true;
}
}
@ -1655,7 +1668,7 @@ bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const {
void MyAvatar::updateOrientation(float deltaTime) {
// Smoothly rotate body with arrow keys
float targetSpeed = _driveKeys[YAW] * _yawSpeed;
float targetSpeed = getDriveKey(YAW) * _yawSpeed;
if (targetSpeed != 0.0f) {
const float ROTATION_RAMP_TIMESCALE = 0.1f;
float blend = deltaTime / ROTATION_RAMP_TIMESCALE;
@ -1684,8 +1697,8 @@ void MyAvatar::updateOrientation(float deltaTime) {
// Comfort Mode: If you press any of the left/right rotation drive keys or input, you'll
// get an instantaneous 15 degree turn. If you keep holding the key down you'll get another
// snap turn every half second.
if (_driveKeys[STEP_YAW] != 0.0f) {
totalBodyYaw += _driveKeys[STEP_YAW];
if (getDriveKey(STEP_YAW) != 0.0f) {
totalBodyYaw += getDriveKey(STEP_YAW);
}
// use head/HMD orientation to turn while flying
@ -1722,7 +1735,7 @@ void MyAvatar::updateOrientation(float deltaTime) {
// update body orientation by movement inputs
setOrientation(getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f))));
getHead()->setBasePitch(getHead()->getBasePitch() + _driveKeys[PITCH] * _pitchSpeed * deltaTime);
getHead()->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime);
if (qApp->isHMDMode()) {
glm::quat orientation = glm::quat_cast(getSensorToWorldMatrix()) * getHMDSensorOrientation();
@ -1756,14 +1769,14 @@ void MyAvatar::updateActionMotor(float deltaTime) {
}
// compute action input
glm::vec3 front = (_driveKeys[TRANSLATE_Z]) * IDENTITY_FRONT;
glm::vec3 right = (_driveKeys[TRANSLATE_X]) * IDENTITY_RIGHT;
glm::vec3 front = (getDriveKey(TRANSLATE_Z)) * IDENTITY_FRONT;
glm::vec3 right = (getDriveKey(TRANSLATE_X)) * IDENTITY_RIGHT;
glm::vec3 direction = front + right;
CharacterController::State state = _characterController.getState();
if (state == CharacterController::State::Hover) {
// we're flying --> support vertical motion
glm::vec3 up = (_driveKeys[TRANSLATE_Y]) * IDENTITY_UP;
glm::vec3 up = (getDriveKey(TRANSLATE_Y)) * IDENTITY_UP;
direction += up;
}
@ -1802,7 +1815,7 @@ void MyAvatar::updateActionMotor(float deltaTime) {
_actionMotorVelocity = MAX_WALKING_SPEED * direction;
}
float boomChange = _driveKeys[ZOOM];
float boomChange = getDriveKey(ZOOM);
_boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange;
_boomLength = glm::clamp<float>(_boomLength, ZOOM_MIN, ZOOM_MAX);
}
@ -1833,11 +1846,11 @@ void MyAvatar::updatePosition(float deltaTime) {
}
// capture the head rotation, in sensor space, when the user first indicates they would like to move/fly.
if (!_hoverReferenceCameraFacingIsCaptured && (fabs(_driveKeys[TRANSLATE_Z]) > 0.1f || fabs(_driveKeys[TRANSLATE_X]) > 0.1f)) {
if (!_hoverReferenceCameraFacingIsCaptured && (fabs(getDriveKey(TRANSLATE_Z)) > 0.1f || fabs(getDriveKey(TRANSLATE_X)) > 0.1f)) {
_hoverReferenceCameraFacingIsCaptured = true;
// transform the camera facing vector into sensor space.
_hoverReferenceCameraFacing = transformVectorFast(glm::inverse(_sensorToWorldMatrix), getHead()->getCameraOrientation() * Vectors::UNIT_Z);
} else if (_hoverReferenceCameraFacingIsCaptured && (fabs(_driveKeys[TRANSLATE_Z]) <= 0.1f && fabs(_driveKeys[TRANSLATE_X]) <= 0.1f)) {
} else if (_hoverReferenceCameraFacingIsCaptured && (fabs(getDriveKey(TRANSLATE_Z)) <= 0.1f && fabs(getDriveKey(TRANSLATE_X)) <= 0.1f)) {
_hoverReferenceCameraFacingIsCaptured = false;
}
}
@ -2093,17 +2106,61 @@ bool MyAvatar::getCharacterControllerEnabled() {
}
void MyAvatar::clearDriveKeys() {
for (int i = 0; i < MAX_DRIVE_KEYS; ++i) {
_driveKeys[i] = 0.0f;
_driveKeys.fill(0.0f);
}
void MyAvatar::setDriveKey(DriveKeys key, float val) {
try {
_driveKeys.at(key) = val;
} catch (const std::exception&) {
qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds";
}
}
float MyAvatar::getDriveKey(DriveKeys key) const {
return isDriveKeyDisabled(key) ? 0.0f : getRawDriveKey(key);
}
float MyAvatar::getRawDriveKey(DriveKeys key) const {
try {
return _driveKeys.at(key);
} catch (const std::exception&) {
qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds";
return 0.0f;
}
}
void MyAvatar::relayDriveKeysToCharacterController() {
if (_driveKeys[TRANSLATE_Y] > 0.0f) {
if (getDriveKey(TRANSLATE_Y) > 0.0f) {
_characterController.jump();
}
}
void MyAvatar::disableDriveKey(DriveKeys key) {
try {
_disabledDriveKeys.set(key);
} catch (const std::exception&) {
qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds";
}
}
void MyAvatar::enableDriveKey(DriveKeys key) {
try {
_disabledDriveKeys.reset(key);
} catch (const std::exception&) {
qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds";
}
}
bool MyAvatar::isDriveKeyDisabled(DriveKeys key) const {
try {
return _disabledDriveKeys.test(key);
} catch (const std::exception&) {
qCCritical(interfaceapp) << Q_FUNC_INFO << ": Index out of bounds";
return true;
}
}
glm::vec3 MyAvatar::getWorldBodyPosition() const {
return transformPoint(_sensorToWorldMatrix, extractTranslation(_bodySensorMatrix));
}
@ -2189,7 +2246,15 @@ QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioList
}
void audioListenModeFromScriptValue(const QScriptValue& object, AudioListenerMode& audioListenerMode) {
audioListenerMode = (AudioListenerMode)object.toUInt16();
audioListenerMode = static_cast<AudioListenerMode>(object.toUInt16());
}
QScriptValue driveKeysToScriptValue(QScriptEngine* engine, const MyAvatar::DriveKeys& driveKeys) {
return driveKeys;
}
void driveKeysFromScriptValue(const QScriptValue& object, MyAvatar::DriveKeys& driveKeys) {
driveKeys = static_cast<MyAvatar::DriveKeys>(object.toUInt16());
}
@ -2382,7 +2447,7 @@ bool MyAvatar::didTeleport() {
}
bool MyAvatar::hasDriveInput() const {
return fabsf(_driveKeys[TRANSLATE_X]) > 0.0f || fabsf(_driveKeys[TRANSLATE_Y]) > 0.0f || fabsf(_driveKeys[TRANSLATE_Z]) > 0.0f;
return fabsf(getDriveKey(TRANSLATE_X)) > 0.0f || fabsf(getDriveKey(TRANSLATE_Y)) > 0.0f || fabsf(getDriveKey(TRANSLATE_Z)) > 0.0f;
}
void MyAvatar::setAway(bool value) {
@ -2498,7 +2563,7 @@ bool MyAvatar::pinJoint(int index, const glm::vec3& position, const glm::quat& o
return false;
}
setPosition(position);
slamPosition(position);
setOrientation(orientation);
_rig->setMaxHipsOffsetLength(0.05f);

View file

@ -12,6 +12,8 @@
#ifndef hifi_MyAvatar_h
#define hifi_MyAvatar_h
#include <bitset>
#include <glm/glm.hpp>
#include <SettingHandle.h>
@ -29,20 +31,6 @@
class AvatarActionHold;
class ModelItemID;
enum DriveKeys {
TRANSLATE_X = 0,
TRANSLATE_Y,
TRANSLATE_Z,
YAW,
STEP_TRANSLATE_X,
STEP_TRANSLATE_Y,
STEP_TRANSLATE_Z,
STEP_YAW,
PITCH,
ZOOM,
MAX_DRIVE_KEYS
};
enum eyeContactTarget {
LEFT_EYE,
RIGHT_EYE,
@ -88,9 +76,26 @@ class MyAvatar : public Avatar {
Q_PROPERTY(bool characterControllerEnabled READ getCharacterControllerEnabled WRITE setCharacterControllerEnabled)
public:
enum DriveKeys {
TRANSLATE_X = 0,
TRANSLATE_Y,
TRANSLATE_Z,
YAW,
STEP_TRANSLATE_X,
STEP_TRANSLATE_Y,
STEP_TRANSLATE_Z,
STEP_YAW,
PITCH,
ZOOM,
MAX_DRIVE_KEYS
};
Q_ENUM(DriveKeys)
explicit MyAvatar(RigPointer rig);
~MyAvatar();
void registerMetaTypes(QScriptEngine* engine);
virtual void simulateAttachments(float deltaTime) override;
AudioListenerMode getAudioListenerModeHead() const { return FROM_HEAD; }
@ -180,9 +185,15 @@ public:
// Set what driving keys are being pressed to control thrust levels
void clearDriveKeys();
void setDriveKeys(int key, float val) { _driveKeys[key] = val; };
void setDriveKey(DriveKeys key, float val);
float getDriveKey(DriveKeys key) const;
Q_INVOKABLE float getRawDriveKey(DriveKeys key) const;
void relayDriveKeysToCharacterController();
Q_INVOKABLE void disableDriveKey(DriveKeys key);
Q_INVOKABLE void enableDriveKey(DriveKeys key);
Q_INVOKABLE bool isDriveKeyDisabled(DriveKeys key) const;
eyeContactTarget getEyeContactTarget();
Q_INVOKABLE glm::vec3 getTrackedHeadPosition() const { return _trackedHeadPosition; }
@ -352,7 +363,6 @@ private:
virtual bool shouldRenderHead(const RenderArgs* renderArgs) const override;
void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; setEnableMeshVisible(shouldRender); }
bool getShouldRenderLocally() const { return _shouldRender; }
bool getDriveKeys(int key) { return _driveKeys[key] != 0.0f; };
bool isMyAvatar() const override { return true; }
virtual int parseDataFromBuffer(const QByteArray& buffer) override;
virtual glm::vec3 getSkeletonPosition() const override;
@ -388,7 +398,9 @@ private:
void clampScaleChangeToDomainLimits(float desiredScale);
glm::mat4 computeCameraRelativeHandControllerMatrix(const glm::mat4& controllerSensorMatrix) const;
float _driveKeys[MAX_DRIVE_KEYS];
std::array<float, MAX_DRIVE_KEYS> _driveKeys;
std::bitset<MAX_DRIVE_KEYS> _disabledDriveKeys;
bool _wasPushing;
bool _isPushing;
bool _isBeingPushed;
@ -541,4 +553,7 @@ private:
QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);
void audioListenModeFromScriptValue(const QScriptValue& object, AudioListenerMode& audioListenerMode);
QScriptValue driveKeysToScriptValue(QScriptEngine* engine, const MyAvatar::DriveKeys& driveKeys);
void driveKeysFromScriptValue(const QScriptValue& object, MyAvatar::DriveKeys& driveKeys);
#endif // hifi_MyAvatar_h

View file

@ -6,26 +6,35 @@
var ANIMATION_FPS = 30;
var ANIMATION_FIRST_FRAME = 1;
var ANIMATION_LAST_FRAME = 10;
var RELEASE_KEYS = ['w', 'a', 's', 'd', 'UP', 'LEFT', 'DOWN', 'RIGHT'];
var RELEASE_TIME = 500; // ms
var RELEASE_DISTANCE = 0.2; // meters
var MAX_IK_ERROR = 30;
var IK_SETTLE_TIME = 250; // ms
var DESKTOP_UI_CHECK_INTERVAL = 100;
var DESKTOP_MAX_DISTANCE = 5;
var SIT_DELAY = 25
var MAX_RESET_DISTANCE = 0.5
var SIT_DELAY = 25;
var MAX_RESET_DISTANCE = 0.5; // meters
var OVERRIDEN_DRIVE_KEYS = [
DriveKeys.TRANSLATE_X,
DriveKeys.TRANSLATE_Y,
DriveKeys.TRANSLATE_Z,
DriveKeys.STEP_TRANSLATE_X,
DriveKeys.STEP_TRANSLATE_Y,
DriveKeys.STEP_TRANSLATE_Z,
];
this.entityID = null;
this.timers = {};
this.animStateHandlerID = null;
this.interval = null;
this.sitDownSettlePeriod = null;
this.lastTimeNoDriveKeys = null;
this.preload = function(entityID) {
this.entityID = entityID;
}
this.unload = function() {
if (Settings.getValue(SETTING_KEY) === this.entityID) {
this.sitUp();
this.standUp();
}
if (this.interval !== null) {
Script.clearInterval(this.interval);
@ -96,6 +105,11 @@
print("Someone is already sitting in that chair.");
return;
}
print("Sitting down (" + this.entityID + ")");
var now = Date.now();
this.sitDownSettlePeriod = now + IK_SETTLE_TIME;
this.lastTimeNoDriveKeys = now;
var previousValue = Settings.getValue(SETTING_KEY);
Settings.setValue(SETTING_KEY, this.entityID);
@ -118,20 +132,17 @@
return { headType: 0 };
}, ["headType"]);
Script.update.connect(this, this.update);
Controller.keyPressEvent.connect(this, this.keyPressed);
Controller.keyReleaseEvent.connect(this, this.keyReleased);
for (var i in RELEASE_KEYS) {
Controller.captureKeyEvents({ text: RELEASE_KEYS[i] });
for (var i in OVERRIDEN_DRIVE_KEYS) {
MyAvatar.disableDriveKey(OVERRIDEN_DRIVE_KEYS[i]);
}
}
this.sitUp = function() {
this.standUp = function() {
print("Standing up (" + this.entityID + ")");
MyAvatar.removeAnimationStateHandler(this.animStateHandlerID);
Script.update.disconnect(this, this.update);
Controller.keyPressEvent.disconnect(this, this.keyPressed);
Controller.keyReleaseEvent.disconnect(this, this.keyReleased);
for (var i in RELEASE_KEYS) {
Controller.releaseKeyEvents({ text: RELEASE_KEYS[i] });
for (var i in OVERRIDEN_DRIVE_KEYS) {
MyAvatar.enableDriveKey(OVERRIDEN_DRIVE_KEYS[i]);
}
this.setSeatUser(null);
@ -156,6 +167,7 @@
}
}
// function called by teleport.js if it detects the appropriate userData
this.sit = function () {
this.sitDown();
}
@ -207,7 +219,33 @@
var properties = Entities.getEntityProperties(this.entityID);
var avatarDistance = Vec3.distance(MyAvatar.position, properties.position);
var ikError = MyAvatar.getIKErrorOnLastSolve();
if (avatarDistance > RELEASE_DISTANCE || ikError > MAX_IK_ERROR) {
var now = Date.now();
var shouldStandUp = false;
// Check if a drive key is pressed
var hasActiveDriveKey = false;
for (var i in OVERRIDEN_DRIVE_KEYS) {
if (MyAvatar.getRawDriveKey(OVERRIDEN_DRIVE_KEYS[i]) != 0.0) {
hasActiveDriveKey = true;
break;
}
}
// Only standup if user has been pushing a drive key for RELEASE_TIME
if (hasActiveDriveKey) {
var elapsed = now - this.lastTimeNoDriveKeys;
shouldStandUp = elapsed > RELEASE_TIME;
} else {
this.lastTimeNoDriveKeys = Date.now();
}
// Allow some time for the IK to settle
if (ikError > MAX_IK_ERROR && now > this.sitDownSettlePeriod) {
shouldStandUp = true;
}
if (shouldStandUp || avatarDistance > RELEASE_DISTANCE) {
print("IK error: " + ikError + ", distance from chair: " + avatarDistance);
// Move avatar in front of the chair to avoid getting stuck in collision hulls
@ -215,45 +253,13 @@
var offset = { x: 0, y: 1.0, z: -0.5 - properties.dimensions.z * properties.registrationPoint.z };
var position = Vec3.sum(properties.position, Vec3.multiplyQbyV(properties.rotation, offset));
MyAvatar.position = position;
print("Moving Avatar in front of the chair.")
}
this.sitUp();
this.standUp();
}
}
}
this.keyPressed = function(event) {
if (isInEditMode()) {
return;
}
if (RELEASE_KEYS.indexOf(event.text) !== -1) {
var that = this;
this.timers[event.text] = Script.setTimeout(function() {
delete that.timers[event.text];
var properties = Entities.getEntityProperties(that.entityID);
var avatarDistance = Vec3.distance(MyAvatar.position, properties.position);
// Move avatar in front of the chair to avoid getting stuck in collision hulls
if (avatarDistance < MAX_RESET_DISTANCE) {
var offset = { x: 0, y: 1.0, z: -0.5 - properties.dimensions.z * properties.registrationPoint.z };
var position = Vec3.sum(properties.position, Vec3.multiplyQbyV(properties.rotation, offset));
MyAvatar.position = position;
}
that.sitUp();
}, RELEASE_TIME);
}
}
this.keyReleased = function(event) {
if (RELEASE_KEYS.indexOf(event.text) !== -1) {
if (this.timers[event.text]) {
Script.clearTimeout(this.timers[event.text]);
delete this.timers[event.text];
}
}
}
this.canSitDesktop = function() {
var properties = Entities.getEntityProperties(this.entityID, ["position"]);
var distanceFromSeat = Vec3.distance(MyAvatar.position, properties.position);