From e4ef3ae8697f8431c8f4191a43eda3b3b2f0d289 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 1 Feb 2016 15:06:53 -0800 Subject: [PATCH 01/15] automatically save adjustments to offset of equipped items --- examples/attachedEntitiesManager.js | 37 ++++++++++++---------- examples/controllers/handControllerGrab.js | 10 +++--- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/examples/attachedEntitiesManager.js b/examples/attachedEntitiesManager.js index f44564d6dc..01f8f861c9 100644 --- a/examples/attachedEntitiesManager.js +++ b/examples/attachedEntitiesManager.js @@ -116,6 +116,7 @@ function AttachedEntitiesManager() { manager.checkIfWearable(parsedMessage.grabbedEntity, parsedMessage.joint) // manager.saveAttachedEntities(); } else if (parsedMessage.action === 'shared-release') { + manager.updateRelativeOffsets(parsedMessage.grabbedEntity); // manager.saveAttachedEntities(); } else if (parsedMessage.action === 'equip') { // manager.saveAttachedEntities(); @@ -173,11 +174,12 @@ function AttachedEntitiesManager() { parentJointIndex: bestJointIndex }; - if (!this.avatarIsInDressingRoom() && - bestJointOffset && bestJointOffset.constructor === Array && bestJointOffset.length > 1) { - // don't snap the entity to the preferred position if the avatar is in the dressing room. - wearProps.localPosition = bestJointOffset[0]; - wearProps.localRotation = bestJointOffset[1]; + if (bestJointOffset && bestJointOffset.constructor === Array && bestJointOffset.length > 1) { + if (!this.avatarIsInDressingRoom()) { + // don't snap the entity to the preferred position if the avatar is in the dressing room. + wearProps.localPosition = bestJointOffset[0]; + wearProps.localRotation = bestJointOffset[1]; + } } Entities.editEntity(grabbedEntity, wearProps); } else if (props.parentID != NULL_UUID) { @@ -189,12 +191,19 @@ function AttachedEntitiesManager() { } } - this.updateRelativeOffsets = function(entityID, props) { + this.updateRelativeOffsets = function(entityID) { // save the preferred (current) relative position and rotation into the user-data of the entity - var wearableData = getEntityCustomData('wearable', entityID, DEFAULT_WEARABLE_DATA); - var currentJointName = MyAvatar.getJointNames()[props.parentJointIndex]; - wearableData.joints[currentJointName] = [props.localPosition, props.localRotation]; - setEntityCustomData('wearable', entityID, wearableData); + var props = Entities.getEntityProperties(entityID); + if (props.parentID == MyAvatar.sessionUUID) { + grabData = getEntityCustomData('grabKey', entityID, {}); + grabbableData = getEntityCustomData('grabbableKey', entityID, {}); + var wearableData = getEntityCustomData('wearable', entityID, DEFAULT_WEARABLE_DATA); + var currentJointName = MyAvatar.getJointNames()[props.parentJointIndex]; + wearableData.joints[currentJointName] = [props.localPosition, props.localRotation]; + setEntityCustomData('wearable', entityID, wearableData); + return true; + } + return false; } this.saveAttachedEntities = function() { @@ -203,12 +212,8 @@ function AttachedEntitiesManager() { var nearbyEntities = Entities.findEntities(MyAvatar.position, ATTACHED_ENTITY_SEARCH_DISTANCE); for (i = 0; i < nearbyEntities.length; i++) { var entityID = nearbyEntities[i]; - var props = Entities.getEntityProperties(entityID); - if (props.parentID == MyAvatar.sessionUUID) { - grabData = getEntityCustomData('grabKey', entityID, {}); - grabbableData = getEntityCustomData('grabbableKey', entityID, {}); - this.updateRelativeOffsets(entityID, props); - props = Entities.getEntityProperties(entityID); // refresh, because updateRelativeOffsets changed them + if (this.updateRelativeOffsets(entityID)) { + var props = Entities.getEntityProperties(entityID); // refresh, because updateRelativeOffsets changed them this.scrubProperties(props); saveData.push(props); } diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 838134cfff..842c54b86d 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1217,10 +1217,12 @@ function MyController(hand) { this.hasPresetOffsets = function() { var wearableData = getEntityCustomData('wearable', this.grabbedEntity, {joints: {}}); - var allowedJoints = wearableData.joints; - var handJointName = this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"; - if (handJointName in allowedJoints) { - return true; + if ("joints" in wearableData) { + var allowedJoints = wearableData.joints; + var handJointName = this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"; + if (handJointName in allowedJoints) { + return true; + } } } From e9fd439ffd0210b0720b084bcecf9a1b91c07eb3 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 28 Jan 2016 18:27:49 -0800 Subject: [PATCH 02/15] Added inAir animations --- .../defaultAvatar_full/avatar-animation.json | 80 +++++++++++++++++-- interface/src/avatar/SkeletonModel.cpp | 9 ++- libraries/animation/src/Rig.cpp | 38 ++++++++- libraries/animation/src/Rig.h | 11 ++- libraries/physics/src/CharacterController.h | 1 + 5 files changed, 125 insertions(+), 14 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index 42ff64abc6..46912120db 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -247,7 +247,8 @@ { "var": "isTurningRight", "state": "turnRight" }, { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, - { "var": "isFlying", "state": "fly" } + { "var": "isFlying", "state": "fly" }, + { "var": "isInAir", "state": "inAir" } ] }, { @@ -263,7 +264,8 @@ { "var": "isTurningRight", "state": "turnRight" }, { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, - { "var": "isFlying", "state": "fly" } + { "var": "isFlying", "state": "fly" }, + { "var": "isInAir", "state": "inAir" } ] }, { @@ -278,7 +280,8 @@ { "var": "isTurningRight", "state": "turnRight" }, { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, - { "var": "isFlying", "state": "fly" } + { "var": "isFlying", "state": "fly" }, + { "var": "isInAir", "state": "inAir" } ] }, { @@ -293,7 +296,8 @@ { "var": "isTurningRight", "state": "turnRight" }, { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, - { "var": "isFlying", "state": "fly" } + { "var": "isFlying", "state": "fly" }, + { "var": "isInAir", "state": "inAir" } ] }, { @@ -308,7 +312,8 @@ { "var": "isTurningRight", "state": "turnRight" }, { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, - { "var": "isFlying", "state": "fly" } + { "var": "isFlying", "state": "fly" }, + { "var": "isInAir", "state": "inAir" } ] }, { @@ -323,7 +328,8 @@ { "var": "isTurningRight", "state": "turnRight" }, { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, - { "var": "isFlying", "state": "fly" } + { "var": "isFlying", "state": "fly" }, + { "var": "isInAir", "state": "inAir" } ] }, { @@ -338,7 +344,8 @@ { "var": "isMovingLeft", "state": "strafeLeft" }, { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, - { "var": "isFlying", "state": "fly" } + { "var": "isFlying", "state": "fly" }, + { "var": "isInAir", "state": "inAir" } ] }, { @@ -353,7 +360,8 @@ { "var": "isMovingLeft", "state": "strafeLeft" }, { "var": "isTurningRight", "state": "turnRight" }, { "var": "isAway", "state": "awayIntro" }, - { "var": "isFlying", "state": "fly" } + { "var": "isFlying", "state": "fly" }, + { "var": "isInAir", "state": "inAir" } ] }, { @@ -385,8 +393,18 @@ "interpTarget": 6, "interpDuration": 6, "transitions": [ + { "var": "isAway", "state": "awayIntro" }, { "var": "isNotFlying", "state": "idle" } ] + }, + { + "id": "inAir", + "interpTarget": 3, + "interpDuration": 3, + "transitions": [ + { "var": "isAway", "state": "awayIntro" }, + { "var": "isNotInAir", "state": "idle" } + ] } ] }, @@ -685,6 +703,52 @@ "loopFlag": true }, "children": [] + }, + { + "id": "inAir", + "type": "blendLinear", + "data": { + "alpha": 0.0, + "alphaVar": "inAirAlpha" + }, + "children": [ + { + "id": "inAirPreApex", + "type": "clip", + "data": { + "url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_in_air.fbx", + "startFrame": 0.0, + "endFrame": 0.0, + "timeScale": 0.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "inAirApex", + "type": "clip", + "data": { + "url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_in_air.fbx", + "startFrame": 6.0, + "endFrame": 6.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "inAirPostApex", + "type": "clip", + "data": { + "url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_in_air.fbx", + "startFrame": 11.0, + "endFrame": 11.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + } + ] } ] } diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 6198a1b0a0..50cbbbbb00 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -133,7 +133,14 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig->updateFromHandParameters(handParams, deltaTime); - _rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation(), myAvatar->isHovering()); + Rig::CharacterControllerState ccState = Rig::CharacterControllerState::Ground; + if (myAvatar->getCharacterController()->isHovering()) { + ccState = Rig::CharacterControllerState::Hover; + } else if (myAvatar->getCharacterController()->isJumping()) { + ccState = Rig::CharacterControllerState::Jump; + } + + _rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation(), ccState); // evaluate AnimGraph animation and update jointStates. Model::updateRig(deltaTime, parentTransform); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 4d72bfbaea..94bc23cf06 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -504,7 +504,7 @@ static const std::vector FORWARD_SPEEDS = { 0.4f, 1.4f, 4.5f }; // m/s static const std::vector BACKWARD_SPEEDS = { 0.6f, 1.45f }; // m/s static const std::vector LATERAL_SPEEDS = { 0.2f, 0.65f }; // m/s -void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation, bool isHovering) { +void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation, CharacterControllerState ccState) { glm::vec3 front = worldRotation * IDENTITY_FRONT; @@ -572,11 +572,16 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos const float TURN_ENTER_SPEED_THRESHOLD = 0.5f; // rad/sec const float TURN_EXIT_SPEED_THRESHOLD = 0.2f; // rad/sec - if (isHovering) { + if (ccState == CharacterControllerState::Hover) { if (_desiredState != RigRole::Hover) { _desiredStateAge = 0.0f; } _desiredState = RigRole::Hover; + } else if (ccState == CharacterControllerState::Jump) { + if (_desiredState != RigRole::Jump) { + _desiredStateAge = 0.0f; + } + _desiredState = RigRole::Jump; } else { float moveThresh; if (_state != RigRole::Move) { @@ -662,6 +667,8 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotTurning", true); _animVars.set("isFlying", false); _animVars.set("isNotFlying", true); + _animVars.set("isInAir", false); + _animVars.set("isNotInAir", true); } } else if (_state == RigRole::Turn) { if (turningSpeed > 0.0f) { @@ -682,6 +689,8 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotMoving", true); _animVars.set("isFlying", false); _animVars.set("isNotFlying", true); + _animVars.set("isInAir", false); + _animVars.set("isNotInAir", true); } else if (_state == RigRole::Idle ) { // default anim vars to notMoving and notTurning _animVars.set("isMovingForward", false); @@ -694,7 +703,9 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotTurning", true); _animVars.set("isFlying", false); _animVars.set("isNotFlying", true); - } else { + _animVars.set("isInAir", false); + _animVars.set("isNotInAir", true); + } else if (_state == RigRole::Hover) { // flying. _animVars.set("isMovingForward", false); _animVars.set("isMovingBackward", false); @@ -706,6 +717,27 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotTurning", true); _animVars.set("isFlying", true); _animVars.set("isNotFlying", false); + _animVars.set("isInAir", false); + _animVars.set("isNotInAir", true); + } else if (_state == RigRole::Jump) { + // jumping in-air + _animVars.set("isMovingForward", false); + _animVars.set("isMovingBackward", false); + _animVars.set("isMovingLeft", false); + _animVars.set("isMovingRight", false); + _animVars.set("isNotMoving", true); + _animVars.set("isTurningLeft", false); + _animVars.set("isTurningRight", false); + _animVars.set("isNotTurning", true); + _animVars.set("isFlying", false); + _animVars.set("isNotFlying", true); + _animVars.set("isInAir", true); + _animVars.set("isNotInAir", false); + + // compute blend based on velocity + const float JUMP_SPEED = 3.5f; + float alpha = glm::clamp(-worldVelocity.y / JUMP_SPEED, -1.0f, 1.0f) + 1.0f; + _animVars.set("inAirAlpha", alpha); } t += deltaTime; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index cf96c6dc16..50d775d7f5 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -73,6 +73,12 @@ public: glm::quat rightOrientation = glm::quat(); // rig space (z forward) }; + enum class CharacterControllerState { + Ground = 0, + Jump, + Hover + }; + virtual ~Rig() {} void destroyAnimGraph(); @@ -141,7 +147,7 @@ public: glm::mat4 getJointTransform(int jointIndex) const; // Start or stop animations as needed. - void computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation, bool isHovering); + void computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation, CharacterControllerState ccState); // Regardless of who started the animations or how many, update the joints. void updateAnimations(float deltaTime, glm::mat4 rootTransform); @@ -271,7 +277,8 @@ public: Idle = 0, Turn, Move, - Hover + Hover, + Jump }; RigRole _state { RigRole::Idle }; RigRole _desiredState { RigRole::Idle }; diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index c1c64a1a02..3c9fc476ca 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -75,6 +75,7 @@ public: glm::vec3 getLinearVelocity() const; + bool isJumping() const { return _isJumping; } bool isHovering() const { return _isHovering; } void setHovering(bool enabled); From 61c55ebf6c96857559280116a830dec7aa5c410a Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 29 Jan 2016 11:57:14 -0800 Subject: [PATCH 03/15] Updated character controller with a state enumeration Also, adjusted checkForSupport logic such that very slanted walls are not considered support. --- interface/src/avatar/MyAvatar.cpp | 7 +-- interface/src/avatar/MyAvatar.h | 2 - .../src/avatar/MyCharacterController.cpp | 2 +- interface/src/avatar/SkeletonModel.cpp | 20 ++++--- libraries/animation/src/Rig.cpp | 8 +-- libraries/animation/src/Rig.h | 4 +- libraries/physics/src/CharacterController.cpp | 60 +++++++++---------- libraries/physics/src/CharacterController.h | 17 +++--- 8 files changed, 62 insertions(+), 58 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0c0b51bda2..0aadaed150 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1510,7 +1510,8 @@ void MyAvatar::updatePosition(float deltaTime) { // rotate velocity into camera frame glm::quat rotation = getHead()->getCameraOrientation(); glm::vec3 localVelocity = glm::inverse(rotation) * _targetVelocity; - glm::vec3 newLocalVelocity = applyKeyboardMotor(deltaTime, localVelocity, isHovering()); + bool isHovering = _characterController.getState() == CharacterController::State::Hover; + glm::vec3 newLocalVelocity = applyKeyboardMotor(deltaTime, localVelocity, isHovering); newLocalVelocity = applyScriptedMotor(deltaTime, newLocalVelocity); // rotate back into world-frame @@ -1579,10 +1580,6 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float return false; } -bool MyAvatar::isHovering() const { - return _characterController.isHovering(); -} - void MyAvatar::increaseSize() { if ((1.0f + SCALING_RATIO) * _targetScale < MAX_AVATAR_SCALE) { _targetScale *= (1.0f + SCALING_RATIO); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 760f390de9..ac77784077 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -239,8 +239,6 @@ public: glm::quat getCustomListenOrientation() { return _customListenOrientation; } void setCustomListenOrientation(glm::quat customListenOrientation) { _customListenOrientation = customListenOrientation; } - bool isHovering() const; - public slots: void increaseSize(); void decreaseSize(); diff --git a/interface/src/avatar/MyCharacterController.cpp b/interface/src/avatar/MyCharacterController.cpp index c7f2945757..07156e9294 100644 --- a/interface/src/avatar/MyCharacterController.cpp +++ b/interface/src/avatar/MyCharacterController.cpp @@ -67,7 +67,7 @@ void MyCharacterController::updateShapeIfNecessary() { _rigidBody->setAngularFactor(0.0f); _rigidBody->setWorldTransform(btTransform(glmToBullet(_avatar->getOrientation()), glmToBullet(_avatar->getPosition()))); - if (_isHovering) { + if (_state == State::Hover) { _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); } else { _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 50cbbbbb00..c8f14578b4 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -72,6 +72,18 @@ void SkeletonModel::initJointStates() { emit skeletonLoaded(); } +Rig::CharacterControllerState convertCharacterControllerState(CharacterController::State state) { + switch (state) { + default: + case CharacterController::State::Ground: + return Rig::CharacterControllerState::Ground; + case CharacterController::State::InAir: + return Rig::CharacterControllerState::InAir; + case CharacterController::State::Hover: + return Rig::CharacterControllerState::Hover; + }; +} + // Called within Model::simulate call, below. void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { Head* head = _owningAvatar->getHead(); @@ -133,13 +145,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig->updateFromHandParameters(handParams, deltaTime); - Rig::CharacterControllerState ccState = Rig::CharacterControllerState::Ground; - if (myAvatar->getCharacterController()->isHovering()) { - ccState = Rig::CharacterControllerState::Hover; - } else if (myAvatar->getCharacterController()->isJumping()) { - ccState = Rig::CharacterControllerState::Jump; - } - + Rig::CharacterControllerState ccState = convertCharacterControllerState(myAvatar->getCharacterController()->getState()); _rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation(), ccState); // evaluate AnimGraph animation and update jointStates. diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 94bc23cf06..33c8f33ac8 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -577,11 +577,11 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _desiredStateAge = 0.0f; } _desiredState = RigRole::Hover; - } else if (ccState == CharacterControllerState::Jump) { - if (_desiredState != RigRole::Jump) { + } else if (ccState == CharacterControllerState::InAir) { + if (_desiredState != RigRole::InAir) { _desiredStateAge = 0.0f; } - _desiredState = RigRole::Jump; + _desiredState = RigRole::InAir; } else { float moveThresh; if (_state != RigRole::Move) { @@ -719,7 +719,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotFlying", false); _animVars.set("isInAir", false); _animVars.set("isNotInAir", true); - } else if (_state == RigRole::Jump) { + } else if (_state == RigRole::InAir) { // jumping in-air _animVars.set("isMovingForward", false); _animVars.set("isMovingBackward", false); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 50d775d7f5..dc07a32efd 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -75,7 +75,7 @@ public: enum class CharacterControllerState { Ground = 0, - Jump, + InAir, Hover }; @@ -278,7 +278,7 @@ public: Turn, Move, Hover, - Jump + InAir }; RigRole _state { RigRole::Idle }; RigRole _desiredState { RigRole::Idle }; diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index a30feec150..055f846793 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -1,4 +1,4 @@ -// + // // CharacterControllerInterface.cpp // libraries/physcis/src // @@ -51,10 +51,7 @@ CharacterController::CharacterController() { _followDesiredBodyTransform.setIdentity(); _followTimeRemaining = 0.0f; _jumpSpeed = JUMP_SPEED; - _isOnGround = false; - _isJumping = false; - _isFalling = false; - _isHovering = true; + _state = State::Hover; _isPushingUp = false; _jumpToHoverStart = 0; _followTime = 0.0f; @@ -107,6 +104,8 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { } } +static const float COS_PI_OVER_THREE = cosf(PI / 3.0f); + bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld) const { int numManifolds = collisionWorld->getDispatcher()->getNumManifolds(); for (int i = 0; i < numManifolds; i++) { @@ -119,8 +118,10 @@ bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld) cons btManifoldPoint& pt = contactManifold->getContactPoint(j); // check to see if contact point is touching the bottom sphere of the capsule. + // and the contact normal is not slanted too much. float contactPointY = (obA == _rigidBody) ? pt.m_localPointA.getY() : pt.m_localPointB.getY(); - if (contactPointY < -_halfHeight) { + btVector3 normal = (obA == _rigidBody) ? pt.m_normalWorldOnB : -pt.m_normalWorldOnB; + if (contactPointY < -_halfHeight && normal.dot(_currentUp) > COS_PI_OVER_THREE) { return true; } } @@ -165,7 +166,7 @@ void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { } const btScalar MIN_SPEED = 0.001f; - if (_isHovering) { + if (_state == State::Hover) { if (desiredSpeed < MIN_SPEED) { if (actualSpeed < MIN_SPEED) { _rigidBody->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f)); @@ -255,9 +256,9 @@ void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { void CharacterController::jump() { // check for case where user is holding down "jump" key... - // we'll eventually tansition to "hover" - if (!_isJumping) { - if (!_isHovering) { + // we'll eventually transition to "hover" + if (_state != State::InAir) { + if (_state != State::Hover) { _jumpToHoverStart = usecTimestampNow(); _pendingFlags |= PENDING_FLAG_JUMP; } @@ -266,7 +267,7 @@ void CharacterController::jump() { const quint64 JUMP_TO_HOVER_PERIOD = 75 * (USECS_PER_SECOND / 100); if (now - _jumpToHoverStart > JUMP_TO_HOVER_PERIOD) { _isPushingUp = true; - setHovering(true); + setState(State::Hover); } } } @@ -276,19 +277,19 @@ bool CharacterController::onGround() const { return _floorDistance < FLOOR_PROXIMITY_THRESHOLD || _hasSupport; } -void CharacterController::setHovering(bool hover) { - if (hover != _isHovering) { - _isHovering = hover; - _isJumping = false; - +void CharacterController::setState(State desiredState) { + if (desiredState == State::Hover && _state != State::Hover) { + // hover enter if (_rigidBody) { - if (hover) { - _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); - } else { - _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); - } + _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); + } + } else if (_state == State::Hover && desiredState != State::Hover) { + // hover exit + if (_rigidBody) { + _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); } } + _state = desiredState; } void CharacterController::setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale) { @@ -335,9 +336,8 @@ void CharacterController::setEnabled(bool enabled) { _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION; } _pendingFlags &= ~ PENDING_FLAG_ADD_TO_SIMULATION; - _isOnGround = false; } - setHovering(true); + setState(State::Hover); _enabled = enabled; } } @@ -345,7 +345,7 @@ void CharacterController::setEnabled(bool enabled) { void CharacterController::updateUpAxis(const glm::quat& rotation) { btVector3 oldUp = _currentUp; _currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS); - if (!_isHovering) { + if (_state != State::Hover) { const btScalar MIN_UP_ERROR = 0.01f; if (oldUp.distance(_currentUp) > MIN_UP_ERROR) { _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); @@ -425,23 +425,23 @@ void CharacterController::preSimulation() { if (rayCallback.hasHit()) { _floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius; const btScalar MIN_HOVER_HEIGHT = 3.0f; - if (_isHovering && _floorDistance < MIN_HOVER_HEIGHT && !_isPushingUp) { - setHovering(false); + if (_state == State::Hover && _floorDistance < MIN_HOVER_HEIGHT && !_isPushingUp) { + setState(State::InAir); } // TODO: use collision events rather than ray-trace test to disable jumping const btScalar JUMP_PROXIMITY_THRESHOLD = 0.1f * _radius; - if (_floorDistance < JUMP_PROXIMITY_THRESHOLD) { - _isJumping = false; + if (_floorDistance < JUMP_PROXIMITY_THRESHOLD || _hasSupport) { + setState(State::Ground); } } else if (!_hasSupport) { _floorDistance = FLT_MAX; - setHovering(true); + setState(State::Hover); } if (_pendingFlags & PENDING_FLAG_JUMP) { _pendingFlags &= ~ PENDING_FLAG_JUMP; if (onGround()) { - _isJumping = true; + setState(State::InAir); btVector3 velocity = _rigidBody->getLinearVelocity(); velocity += _jumpSpeed * _currentUp; _rigidBody->setLinearVelocity(velocity); diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 3c9fc476ca..c8b360237d 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -75,9 +75,13 @@ public: glm::vec3 getLinearVelocity() const; - bool isJumping() const { return _isJumping; } - bool isHovering() const { return _isHovering; } - void setHovering(bool enabled); + enum class State { + Ground = 0, + InAir, + Hover + }; + + State getState() const { return _state; } void setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale); @@ -87,6 +91,8 @@ public: bool getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation); protected: + void setState(State state); + void updateUpAxis(const glm::quat& rotation); bool checkForSupport(btCollisionWorld* collisionWorld) const; @@ -117,10 +123,7 @@ protected: btQuaternion _followAngularDisplacement; bool _enabled; - bool _isOnGround; - bool _isJumping; - bool _isFalling; - bool _isHovering; + State _state; bool _isPushingUp; btDynamicsWorld* _dynamicsWorld { nullptr }; From 2936811484541b5b6133f8e1560393a70a3a8e56 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 29 Jan 2016 18:05:18 -0800 Subject: [PATCH 04/15] Added takeoff animation, WIP Character controller still has some jump/in-air bugs. --- .../defaultAvatar_full/avatar-animation.json | 39 ++++++++++++++++--- interface/src/avatar/SkeletonModel.cpp | 2 + libraries/animation/src/Rig.cpp | 35 +++++++++++++++++ libraries/animation/src/Rig.h | 2 + libraries/physics/src/CharacterController.cpp | 33 ++++++++++------ libraries/physics/src/CharacterController.h | 2 + 6 files changed, 97 insertions(+), 16 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index 46912120db..37da0c37cc 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -248,6 +248,7 @@ { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, { "var": "isFlying", "state": "fly" }, + { "var": "isTakeoff", "state": "takeoff" }, { "var": "isInAir", "state": "inAir" } ] }, @@ -265,6 +266,7 @@ { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, { "var": "isFlying", "state": "fly" }, + { "var": "isTakeoff", "state": "takeoff" }, { "var": "isInAir", "state": "inAir" } ] }, @@ -281,6 +283,7 @@ { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, { "var": "isFlying", "state": "fly" }, + { "var": "isTakeoff", "state": "takeoff" }, { "var": "isInAir", "state": "inAir" } ] }, @@ -297,6 +300,7 @@ { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, { "var": "isFlying", "state": "fly" }, + { "var": "isTakeoff", "state": "takeoff" }, { "var": "isInAir", "state": "inAir" } ] }, @@ -313,6 +317,7 @@ { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, { "var": "isFlying", "state": "fly" }, + { "var": "isTakeoff", "state": "takeoff" }, { "var": "isInAir", "state": "inAir" } ] }, @@ -329,6 +334,7 @@ { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, { "var": "isFlying", "state": "fly" }, + { "var": "isTakeoff", "state": "takeoff" }, { "var": "isInAir", "state": "inAir" } ] }, @@ -345,6 +351,7 @@ { "var": "isTurningLeft", "state": "turnLeft" }, { "var": "isAway", "state": "awayIntro" }, { "var": "isFlying", "state": "fly" }, + { "var": "isTakeoff", "state": "takeoff" }, { "var": "isInAir", "state": "inAir" } ] }, @@ -361,6 +368,7 @@ { "var": "isTurningRight", "state": "turnRight" }, { "var": "isAway", "state": "awayIntro" }, { "var": "isFlying", "state": "fly" }, + { "var": "isTakeoff", "state": "takeoff" }, { "var": "isInAir", "state": "inAir" } ] }, @@ -397,10 +405,19 @@ { "var": "isNotFlying", "state": "idle" } ] }, + { + "id": "takeoff", + "interpTarget": 0, + "interpDuration": 6, + "transitions": [ + { "var": "isAway", "state": "awayIntro" }, + { "var": "isNotTakeoff", "state": "inAir" } + ] + }, { "id": "inAir", - "interpTarget": 3, - "interpDuration": 3, + "interpTarget": 0, + "interpDuration": 6, "transitions": [ { "var": "isAway", "state": "awayIntro" }, { "var": "isNotInAir", "state": "idle" } @@ -704,6 +721,18 @@ }, "children": [] }, + { + "id": "takeoff", + "type": "clip", + "data": { + "url": "https://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims_160127/jump_takeoff.fbx", + "startFrame": 1.0, + "endFrame": 2.5, + "timeScale": 1.0, + "loopFlag": false + }, + "children": [] + }, { "id": "inAir", "type": "blendLinear", @@ -720,7 +749,7 @@ "startFrame": 0.0, "endFrame": 0.0, "timeScale": 0.0, - "loopFlag": true + "loopFlag": false }, "children": [] }, @@ -732,7 +761,7 @@ "startFrame": 6.0, "endFrame": 6.0, "timeScale": 1.0, - "loopFlag": true + "loopFlag": false }, "children": [] }, @@ -744,7 +773,7 @@ "startFrame": 11.0, "endFrame": 11.0, "timeScale": 1.0, - "loopFlag": true + "loopFlag": false }, "children": [] } diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index c8f14578b4..92bdef686d 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -77,6 +77,8 @@ Rig::CharacterControllerState convertCharacterControllerState(CharacterControlle default: case CharacterController::State::Ground: return Rig::CharacterControllerState::Ground; + case CharacterController::State::Takeoff: + return Rig::CharacterControllerState::Takeoff; case CharacterController::State::InAir: return Rig::CharacterControllerState::InAir; case CharacterController::State::Hover: diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 33c8f33ac8..9e811fa358 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -582,6 +582,11 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _desiredStateAge = 0.0f; } _desiredState = RigRole::InAir; + } else if (ccState == CharacterControllerState::Takeoff) { + if (_desiredState != RigRole::Takeoff) { + _desiredStateAge = 0.0f; + } + _desiredState = RigRole::Takeoff; } else { float moveThresh; if (_state != RigRole::Move) { @@ -667,6 +672,8 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotTurning", true); _animVars.set("isFlying", false); _animVars.set("isNotFlying", true); + _animVars.set("isTakeoff", false); + _animVars.set("isNotTakeoff", true); _animVars.set("isInAir", false); _animVars.set("isNotInAir", true); } @@ -689,8 +696,11 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotMoving", true); _animVars.set("isFlying", false); _animVars.set("isNotFlying", true); + _animVars.set("isTakeoff", false); + _animVars.set("isNotTakeoff", true); _animVars.set("isInAir", false); _animVars.set("isNotInAir", true); + } else if (_state == RigRole::Idle ) { // default anim vars to notMoving and notTurning _animVars.set("isMovingForward", false); @@ -703,8 +713,11 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotTurning", true); _animVars.set("isFlying", false); _animVars.set("isNotFlying", true); + _animVars.set("isTakeoff", false); + _animVars.set("isNotTakeoff", true); _animVars.set("isInAir", false); _animVars.set("isNotInAir", true); + } else if (_state == RigRole::Hover) { // flying. _animVars.set("isMovingForward", false); @@ -717,8 +730,28 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotTurning", true); _animVars.set("isFlying", true); _animVars.set("isNotFlying", false); + _animVars.set("isTakeoff", false); + _animVars.set("isNotTakeoff", true); _animVars.set("isInAir", false); _animVars.set("isNotInAir", true); + + } else if (_state == RigRole::Takeoff) { + // jumping in-air + _animVars.set("isMovingForward", false); + _animVars.set("isMovingBackward", false); + _animVars.set("isMovingLeft", false); + _animVars.set("isMovingRight", false); + _animVars.set("isNotMoving", true); + _animVars.set("isTurningLeft", false); + _animVars.set("isTurningRight", false); + _animVars.set("isNotTurning", true); + _animVars.set("isFlying", false); + _animVars.set("isNotFlying", true); + _animVars.set("isTakeoff", true); + _animVars.set("isNotTakeoff", false); + _animVars.set("isInAir", true); + _animVars.set("isNotInAir", false); + } else if (_state == RigRole::InAir) { // jumping in-air _animVars.set("isMovingForward", false); @@ -731,6 +764,8 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _animVars.set("isNotTurning", true); _animVars.set("isFlying", false); _animVars.set("isNotFlying", true); + _animVars.set("isTakeoff", false); + _animVars.set("isNotTakeoff", true); _animVars.set("isInAir", true); _animVars.set("isNotInAir", false); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index dc07a32efd..e12af16e41 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -75,6 +75,7 @@ public: enum class CharacterControllerState { Ground = 0, + Takeoff, InAir, Hover }; @@ -278,6 +279,7 @@ public: Turn, Move, Hover, + Takeoff, InAir }; RigRole _state { RigRole::Idle }; diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 055f846793..93b924d9bc 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -54,6 +54,7 @@ CharacterController::CharacterController() { _state = State::Hover; _isPushingUp = false; _jumpToHoverStart = 0; + _takeoffToInAirStart = 0; _followTime = 0.0f; _followLinearDisplacement = btVector3(0, 0, 0); _followAngularDisplacement = btQuaternion::getIdentity(); @@ -257,18 +258,18 @@ void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { void CharacterController::jump() { // check for case where user is holding down "jump" key... // we'll eventually transition to "hover" - if (_state != State::InAir) { - if (_state != State::Hover) { - _jumpToHoverStart = usecTimestampNow(); - _pendingFlags |= PENDING_FLAG_JUMP; - } - } else { + if (_state == State::Hover) { quint64 now = usecTimestampNow(); const quint64 JUMP_TO_HOVER_PERIOD = 75 * (USECS_PER_SECOND / 100); if (now - _jumpToHoverStart > JUMP_TO_HOVER_PERIOD) { _isPushingUp = true; setState(State::Hover); } + } else { + if (_state != State::Takeoff) { + _jumpToHoverStart = usecTimestampNow(); + _pendingFlags |= PENDING_FLAG_JUMP; + } } } @@ -430,7 +431,7 @@ void CharacterController::preSimulation() { } // TODO: use collision events rather than ray-trace test to disable jumping const btScalar JUMP_PROXIMITY_THRESHOLD = 0.1f * _radius; - if (_floorDistance < JUMP_PROXIMITY_THRESHOLD || _hasSupport) { + if (_state != State::Takeoff && (_floorDistance < JUMP_PROXIMITY_THRESHOLD || _hasSupport)) { setState(State::Ground); } } else if (!_hasSupport) { @@ -438,15 +439,25 @@ void CharacterController::preSimulation() { setState(State::Hover); } + quint64 now = usecTimestampNow(); + if (_pendingFlags & PENDING_FLAG_JUMP) { _pendingFlags &= ~ PENDING_FLAG_JUMP; if (onGround()) { - setState(State::InAir); - btVector3 velocity = _rigidBody->getLinearVelocity(); - velocity += _jumpSpeed * _currentUp; - _rigidBody->setLinearVelocity(velocity); + setState(State::Takeoff); + _takeoffToInAirStart = now; } } + + const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 200 * MSECS_PER_SECOND; + if (_state == State::Takeoff && (now - _takeoffToInAirStart) > TAKE_OFF_TO_IN_AIR_PERIOD) { + setState(State::InAir); + + _takeoffToInAirStart = now + USECS_PER_SECOND * 86500.0f; + btVector3 velocity = _rigidBody->getLinearVelocity(); + velocity += _jumpSpeed * _currentUp; + _rigidBody->setLinearVelocity(velocity); + } } _followTime = 0.0f; diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index c8b360237d..5470104a1a 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -77,6 +77,7 @@ public: enum class State { Ground = 0, + Takeoff, InAir, Hover }; @@ -107,6 +108,7 @@ protected: glm::vec3 _boxScale; // used to compute capsule shape + quint64 _takeoffToInAirStart; quint64 _jumpToHoverStart; btScalar _halfHeight; From 47f3ce3786e97f744d7338bd5cc694511df05891 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 1 Feb 2016 14:18:30 -0800 Subject: [PATCH 05/15] CharacterController jump is more reliable. --- libraries/animation/src/Rig.cpp | 7 ++ libraries/physics/src/CharacterController.cpp | 80 +++++++++++++------ 2 files changed, 61 insertions(+), 26 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 9e811fa358..64935691c8 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -624,6 +624,13 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos const float STATE_CHANGE_HYSTERESIS_TIMER = 0.1f; + // Skip hystersis timer for jump transitions. + if (_desiredState == RigRole::Takeoff) { + _desiredStateAge = STATE_CHANGE_HYSTERESIS_TIMER; + } else if (_state == RigRole::InAir && _desiredState != RigRole::InAir) { + _desiredStateAge = STATE_CHANGE_HYSTERESIS_TIMER; + } + if ((_desiredStateAge >= STATE_CHANGE_HYSTERESIS_TIMER) && _desiredState != _state) { _state = _desiredState; _desiredStateAge = 0.0f; diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 93b924d9bc..ad6c9545d2 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -1,4 +1,4 @@ - // +// // CharacterControllerInterface.cpp // libraries/physcis/src // @@ -15,11 +15,14 @@ #include "PhysicsCollisionGroups.h" #include "ObjectMotionState.h" +#include "PhysicsLogging.h" const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f); const float JUMP_SPEED = 3.5f; const float MAX_FALL_HEIGHT = 20.0f; +#define DEBUG_STATE_CHANGE + // helper class for simple ray-traces from character class ClosestNotMe : public btCollisionWorld::ClosestRayResultCallback { public: @@ -220,7 +223,7 @@ void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { // Dynamicaly compute a follow velocity to move this body toward the _followDesiredBodyTransform. // Rather then add this velocity to velocity the RigidBody, we explicitly teleport the RigidBody towards its goal. // This mirrors the computation done in MyAvatar::FollowHelper::postPhysicsUpdate(). - // These two computations must be kept in sync. + const float MINIMUM_TIME_REMAINING = 0.005f; const float MAX_DISPLACEMENT = 0.5f * _radius; _followTimeRemaining -= dt; @@ -258,18 +261,21 @@ void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { void CharacterController::jump() { // check for case where user is holding down "jump" key... // we'll eventually transition to "hover" - if (_state == State::Hover) { - quint64 now = usecTimestampNow(); - const quint64 JUMP_TO_HOVER_PERIOD = 75 * (USECS_PER_SECOND / 100); - if (now - _jumpToHoverStart > JUMP_TO_HOVER_PERIOD) { - _isPushingUp = true; - setState(State::Hover); - } - } else { - if (_state != State::Takeoff) { - _jumpToHoverStart = usecTimestampNow(); + if (_state != State::Takeoff) { + if (_state == State::InAir) { + quint64 now = usecTimestampNow(); + if (!_isPushingUp) { + _isPushingUp = true; + _jumpToHoverStart = now; + } + const quint64 JUMP_TO_HOVER_PERIOD = 750 * MSECS_PER_SECOND; + if (now - _jumpToHoverStart > JUMP_TO_HOVER_PERIOD) { + setState(State::Hover); + } + } else { _pendingFlags |= PENDING_FLAG_JUMP; } + } } @@ -278,19 +284,41 @@ bool CharacterController::onGround() const { return _floorDistance < FLOOR_PROXIMITY_THRESHOLD || _hasSupport; } -void CharacterController::setState(State desiredState) { - if (desiredState == State::Hover && _state != State::Hover) { - // hover enter - if (_rigidBody) { - _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); - } - } else if (_state == State::Hover && desiredState != State::Hover) { - // hover exit - if (_rigidBody) { - _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); - } +#ifdef DEBUG_STATE_CHANGE +static const char* stateToStr(CharacterController::State state) { + switch (state) { + case CharacterController::State::Ground: + return "Ground"; + case CharacterController::State::Takeoff: + return "Takeoff"; + case CharacterController::State::InAir: + return "InAir"; + case CharacterController::State::Hover: + return "Hover"; + default: + return "Unknown"; + } +} +#endif // #ifdef DEBUG_STATE_CHANGE + +void CharacterController::setState(State desiredState) { + if (desiredState != _state) { +#ifdef DEBUG_STATE_CHANGE + qCDebug(physics) << "CharacterController::setState" << stateToStr(desiredState) << "<-" << stateToStr(_state); +#endif + if (desiredState == State::Hover && _state != State::Hover) { + // hover enter + if (_rigidBody) { + _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); + } + } else if (_state == State::Hover && desiredState != State::Hover) { + // hover exit + if (_rigidBody) { + _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); + } + } + _state = desiredState; } - _state = desiredState; } void CharacterController::setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale) { @@ -411,6 +439,7 @@ void CharacterController::preSimulation() { if (_enabled && _dynamicsWorld) { // slam body to where it is supposed to be _rigidBody->setWorldTransform(_characterBodyTransform); + btVector3 velocity = _rigidBody->getLinearVelocity(); // scan for distant floor // rayStart is at center of bottom sphere @@ -431,7 +460,7 @@ void CharacterController::preSimulation() { } // TODO: use collision events rather than ray-trace test to disable jumping const btScalar JUMP_PROXIMITY_THRESHOLD = 0.1f * _radius; - if (_state != State::Takeoff && (_floorDistance < JUMP_PROXIMITY_THRESHOLD || _hasSupport)) { + if (_state != State::Takeoff && (velocity.dot(_currentUp) <= (JUMP_SPEED / 2.0f)) && ((_floorDistance < JUMP_PROXIMITY_THRESHOLD) || _hasSupport)) { setState(State::Ground); } } else if (!_hasSupport) { @@ -454,7 +483,6 @@ void CharacterController::preSimulation() { setState(State::InAir); _takeoffToInAirStart = now + USECS_PER_SECOND * 86500.0f; - btVector3 velocity = _rigidBody->getLinearVelocity(); velocity += _jumpSpeed * _currentUp; _rigidBody->setLinearVelocity(velocity); } From 8b5cf3e49abbd8f4d6f32edb1f1f06cbd8989b4b Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 1 Feb 2016 20:43:41 -0800 Subject: [PATCH 06/15] CharacterController: refined state machine. --- libraries/physics/src/CharacterController.cpp | 201 +++++++++--------- libraries/physics/src/CharacterController.h | 6 +- 2 files changed, 109 insertions(+), 98 deletions(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index ad6c9545d2..f241be8b35 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -56,7 +56,8 @@ CharacterController::CharacterController() { _jumpSpeed = JUMP_SPEED; _state = State::Hover; _isPushingUp = false; - _jumpToHoverStart = 0; + _jumpButtonDownStart = 0; + _jumpButtonDownCount = 0; _takeoffToInAirStart = 0; _followTime = 0.0f; _followLinearDisplacement = btVector3(0, 0, 0); @@ -158,68 +159,57 @@ void CharacterController::preStep(btCollisionWorld* collisionWorld) { } void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { - btVector3 actualVelocity = _rigidBody->getLinearVelocity(); - btScalar actualSpeed = actualVelocity.length(); - - btVector3 desiredVelocity = _walkVelocity; - btScalar desiredSpeed = desiredVelocity.length(); - - const btScalar MIN_UP_PUSH = 0.1f; - if (desiredVelocity.dot(_currentUp) < MIN_UP_PUSH) { - _isPushingUp = false; - } const btScalar MIN_SPEED = 0.001f; - if (_state == State::Hover) { - if (desiredSpeed < MIN_SPEED) { - if (actualSpeed < MIN_SPEED) { - _rigidBody->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f)); - } else { - const btScalar HOVER_BRAKING_TIMESCALE = 0.1f; - btScalar tau = glm::max(dt / HOVER_BRAKING_TIMESCALE, 1.0f); - _rigidBody->setLinearVelocity((1.0f - tau) * actualVelocity); - } - } else { - const btScalar HOVER_ACCELERATION_TIMESCALE = 0.1f; - btScalar tau = dt / HOVER_ACCELERATION_TIMESCALE; - _rigidBody->setLinearVelocity(actualVelocity - tau * (actualVelocity - desiredVelocity)); + + btVector3 actualVelocity = _rigidBody->getLinearVelocity(); + if (actualVelocity.length() < MIN_SPEED) { + actualVelocity = btVector3(0.0f, 0.0f, 0.0f); + } + + btVector3 desiredVelocity = _walkVelocity; + if (desiredVelocity.length() < MIN_SPEED) { + desiredVelocity = btVector3(0.0f, 0.0f, 0.0f); + } + + // decompose into horizontal and vertical components. + btVector3 actualVertVelocity = actualVelocity.dot(_currentUp) * _currentUp; + btVector3 actualHorizVelocity = actualVelocity - actualVertVelocity; + btVector3 desiredVertVelocity = desiredVelocity.dot(_currentUp) * _currentUp; + btVector3 desiredHorizVelocity = desiredVelocity - desiredVertVelocity; + + btVector3 finalVelocity; + + switch (_state) { + case State::Ground: + case State::Takeoff: + { + // horizontal ground control + const btScalar WALK_ACCELERATION_TIMESCALE = 0.1f; + btScalar tau = dt / WALK_ACCELERATION_TIMESCALE; + finalVelocity = tau * desiredHorizVelocity + (1.0f - tau) * actualHorizVelocity + actualVertVelocity; } - } else { - if (onGround()) { - // walking on ground - if (desiredSpeed < MIN_SPEED) { - if (actualSpeed < MIN_SPEED) { - _rigidBody->setLinearVelocity(btVector3(0.0f, 0.0f, 0.0f)); - } else { - const btScalar HOVER_BRAKING_TIMESCALE = 0.1f; - btScalar tau = dt / HOVER_BRAKING_TIMESCALE; - _rigidBody->setLinearVelocity((1.0f - tau) * actualVelocity); - } - } else { - // TODO: modify desiredVelocity using floor normal - const btScalar WALK_ACCELERATION_TIMESCALE = 0.1f; - btScalar tau = dt / WALK_ACCELERATION_TIMESCALE; - btVector3 velocityCorrection = tau * (desiredVelocity - actualVelocity); - // subtract vertical component - velocityCorrection -= velocityCorrection.dot(_currentUp) * _currentUp; - _rigidBody->setLinearVelocity(actualVelocity + velocityCorrection); - } - } else { - // transitioning to flying - btVector3 velocityCorrection = desiredVelocity - actualVelocity; + break; + case State::InAir: + { + // horizontal air control + const btScalar IN_AIR_ACCELERATION_TIMESCALE = 2.0f; + btScalar tau = dt / IN_AIR_ACCELERATION_TIMESCALE; + finalVelocity = tau * desiredHorizVelocity + (1.0f - tau) * actualHorizVelocity + actualVertVelocity; + } + break; + case State::Hover: + { + // vertical and horizontal air control const btScalar FLY_ACCELERATION_TIMESCALE = 0.2f; btScalar tau = dt / FLY_ACCELERATION_TIMESCALE; - if (!_isPushingUp) { - // actually falling --> compute a different velocity attenuation factor - const btScalar FALL_ACCELERATION_TIMESCALE = 2.0f; - tau = dt / FALL_ACCELERATION_TIMESCALE; - // zero vertical component - velocityCorrection -= velocityCorrection.dot(_currentUp) * _currentUp; - } - _rigidBody->setLinearVelocity(actualVelocity + tau * velocityCorrection); + finalVelocity = tau * desiredVelocity + (1.0f - tau) * actualVelocity; } + break; } + _rigidBody->setLinearVelocity(finalVelocity); + // Dynamicaly compute a follow velocity to move this body toward the _followDesiredBodyTransform. // Rather then add this velocity to velocity the RigidBody, we explicitly teleport the RigidBody towards its goal. // This mirrors the computation done in MyAvatar::FollowHelper::postPhysicsUpdate(). @@ -259,24 +249,7 @@ void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { } void CharacterController::jump() { - // check for case where user is holding down "jump" key... - // we'll eventually transition to "hover" - if (_state != State::Takeoff) { - if (_state == State::InAir) { - quint64 now = usecTimestampNow(); - if (!_isPushingUp) { - _isPushingUp = true; - _jumpToHoverStart = now; - } - const quint64 JUMP_TO_HOVER_PERIOD = 750 * MSECS_PER_SECOND; - if (now - _jumpToHoverStart > JUMP_TO_HOVER_PERIOD) { - setState(State::Hover); - } - } else { - _pendingFlags |= PENDING_FLAG_JUMP; - } - - } + _pendingFlags |= PENDING_FLAG_JUMP; } bool CharacterController::onGround() const { @@ -441,6 +414,9 @@ void CharacterController::preSimulation() { _rigidBody->setWorldTransform(_characterBodyTransform); btVector3 velocity = _rigidBody->getLinearVelocity(); + btVector3 actualVertVelocity = velocity.dot(_currentUp) * _currentUp; + btVector3 actualHorizVelocity = velocity - actualVertVelocity; + // scan for distant floor // rayStart is at center of bottom sphere btVector3 rayStart = _characterBodyTransform.getOrigin() - _halfHeight * _currentUp; @@ -454,41 +430,74 @@ void CharacterController::preSimulation() { _dynamicsWorld->rayTest(rayStart, rayEnd, rayCallback); if (rayCallback.hasHit()) { _floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius; - const btScalar MIN_HOVER_HEIGHT = 3.0f; - if (_state == State::Hover && _floorDistance < MIN_HOVER_HEIGHT && !_isPushingUp) { - setState(State::InAir); - } - // TODO: use collision events rather than ray-trace test to disable jumping - const btScalar JUMP_PROXIMITY_THRESHOLD = 0.1f * _radius; - if (_state != State::Takeoff && (velocity.dot(_currentUp) <= (JUMP_SPEED / 2.0f)) && ((_floorDistance < JUMP_PROXIMITY_THRESHOLD) || _hasSupport)) { - setState(State::Ground); - } - } else if (!_hasSupport) { + } else { _floorDistance = FLT_MAX; - setState(State::Hover); } + const btScalar JUMP_PROXIMITY_THRESHOLD = 0.1f * _radius; + const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 200 * MSECS_PER_SECOND; + const btScalar MIN_HOVER_HEIGHT = 2.5f; + const quint64 JUMP_TO_HOVER_PERIOD = 750 * MSECS_PER_SECOND; + const btScalar UPWARD_VELOCITY_THRESHOLD = 0.05f; + const btScalar MAX_FLYING_SPEED = 30.0f; + quint64 now = usecTimestampNow(); - if (_pendingFlags & PENDING_FLAG_JUMP) { - _pendingFlags &= ~ PENDING_FLAG_JUMP; - if (onGround()) { - setState(State::Takeoff); - _takeoffToInAirStart = now; + // record a time stamp when the jump button was first pressed. + if ((_previousFlags & PENDING_FLAG_JUMP) != (_pendingFlags & PENDING_FLAG_JUMP)) { + if (_pendingFlags & PENDING_FLAG_JUMP) { + _jumpButtonDownStart = now; + _jumpButtonDownCount++; } } - const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 200 * MSECS_PER_SECOND; - if (_state == State::Takeoff && (now - _takeoffToInAirStart) > TAKE_OFF_TO_IN_AIR_PERIOD) { - setState(State::InAir); + bool jumpButtonHeld = _pendingFlags & PENDING_FLAG_JUMP; + bool wantsToGoUp = !jumpButtonHeld && _walkVelocity.dot(_currentUp) > UPWARD_VELOCITY_THRESHOLD; + bool tooFast = actualHorizVelocity.length() > (MAX_FLYING_SPEED / 2.0f); - _takeoffToInAirStart = now + USECS_PER_SECOND * 86500.0f; - velocity += _jumpSpeed * _currentUp; - _rigidBody->setLinearVelocity(velocity); + switch (_state) { + case State::Ground: + if (!rayCallback.hasHit() && !_hasSupport) { + setState(State::Hover); + } else if (_pendingFlags & PENDING_FLAG_JUMP) { + _takeOffJumpButtonID = _jumpButtonDownCount; + setState(State::Takeoff); + } + break; + case State::Takeoff: + if (!rayCallback.hasHit() && !_hasSupport) { + setState(State::Hover); + } else if ((now - _takeoffToInAirStart) > TAKE_OFF_TO_IN_AIR_PERIOD) { + setState(State::InAir); + _takeoffToInAirStart = now + USECS_PER_SECOND * 86500.0f; + velocity += _jumpSpeed * _currentUp; + _rigidBody->setLinearVelocity(velocity); + } + break; + case State::InAir: { + if ((velocity.dot(_currentUp) <= (JUMP_SPEED / 2.0f)) && ((_floorDistance < JUMP_PROXIMITY_THRESHOLD) || _hasSupport)) { + setState(State::Ground); + } else if ((jumpButtonHeld && ((_takeOffJumpButtonID != _jumpButtonDownCount) || (now - _jumpButtonDownStart) > JUMP_TO_HOVER_PERIOD)) || wantsToGoUp) { + setState(State::Hover); + } + break; + } + case State::Hover: + if (!jumpButtonHeld && !wantsToGoUp && _floorDistance < MIN_HOVER_HEIGHT && !tooFast) { + setState(State::InAir); + } else if (((_floorDistance < JUMP_PROXIMITY_THRESHOLD) || _hasSupport) && !tooFast) { + setState(State::Ground); + } + break; } } - _followTime = 0.0f; + /// _walkVelocity.dot(_currentUp) > UPWARD_VELOCITY_THRESHOLD + + _previousFlags = _pendingFlags; + _pendingFlags &= ~PENDING_FLAG_JUMP; + + _followTime = 0.0f; _followLinearDisplacement = btVector3(0, 0, 0); _followAngularDisplacement = btQuaternion::getIdentity(); } diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 5470104a1a..97bda5c5d5 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -109,7 +109,9 @@ protected: glm::vec3 _boxScale; // used to compute capsule shape quint64 _takeoffToInAirStart; - quint64 _jumpToHoverStart; + quint64 _jumpButtonDownStart; + quint32 _jumpButtonDownCount; + quint32 _takeOffJumpButtonID; btScalar _halfHeight; btScalar _radius; @@ -131,7 +133,7 @@ protected: btDynamicsWorld* _dynamicsWorld { nullptr }; btRigidBody* _rigidBody { nullptr }; uint32_t _pendingFlags { 0 }; - + uint32_t _previousFlags { 0 }; }; #endif // hifi_CharacterControllerInterface_h From c4e1509aa214948bbe2428714d71c9f5133f5186 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 2 Feb 2016 11:19:17 -0800 Subject: [PATCH 07/15] CharacterController: better debug support of internal state machine Head is no longer visible when flying fast, in first-person HMD mode. --- interface/src/avatar/MyAvatar.cpp | 12 +++-- libraries/physics/src/CharacterController.cpp | 46 +++++++++++-------- libraries/physics/src/CharacterController.h | 6 +++ 3 files changed, 40 insertions(+), 24 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0aadaed150..4253091533 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1311,21 +1311,23 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { _prevShouldDrawHead = shouldDrawHead; } -const float RENDER_HEAD_CUTOFF_DISTANCE = 0.50f; +const float RENDER_HEAD_CUTOFF_DISTANCE = 0.5f; bool MyAvatar::cameraInsideHead() const { const Head* head = getHead(); const glm::vec3 cameraPosition = qApp->getCamera()->getPosition(); - return glm::length(cameraPosition - head->getEyePosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getUniformScale()); + return glm::length(cameraPosition - getDefaultEyePosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getUniformScale()); } bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { - return ((renderArgs->_renderMode != RenderArgs::DEFAULT_RENDER_MODE) || - (qApp->getCamera()->getMode() != CAMERA_MODE_FIRST_PERSON) || - !cameraInsideHead()); + bool defaultMode = renderArgs->_renderMode == RenderArgs::DEFAULT_RENDER_MODE; + bool firstPerson = qApp->getCamera()->getMode() == CAMERA_MODE_FIRST_PERSON; + bool insideHead = cameraInsideHead(); + return !defaultMode || !firstPerson || !insideHead; } void MyAvatar::updateOrientation(float deltaTime) { + // Smoothly rotate body with arrow keys float targetSpeed = _driveKeys[YAW] * _yawSpeed; if (targetSpeed != 0.0f) { diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index f241be8b35..160b227127 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -21,7 +21,11 @@ const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f); const float JUMP_SPEED = 3.5f; const float MAX_FALL_HEIGHT = 20.0f; -#define DEBUG_STATE_CHANGE +#ifdef DEBUG_STATE_CHANGE +#define SET_STATE(desiredState, reason) setState(desiredState, reason) +#else +#define SET_STATE(desiredState, reason) setState(desiredState) +#endif // helper class for simple ray-traces from character class ClosestNotMe : public btCollisionWorld::ClosestRayResultCallback { @@ -65,7 +69,6 @@ CharacterController::CharacterController() { _hasSupport = false; _pendingFlags = PENDING_FLAG_UPDATE_SHAPE; - } bool CharacterController::needsRemoval() const { @@ -274,10 +277,14 @@ static const char* stateToStr(CharacterController::State state) { } #endif // #ifdef DEBUG_STATE_CHANGE +#ifdef DEBUG_STATE_CHANGE +void CharacterController::setState(State desiredState, const char* reason) { +#else void CharacterController::setState(State desiredState) { +#endif if (desiredState != _state) { #ifdef DEBUG_STATE_CHANGE - qCDebug(physics) << "CharacterController::setState" << stateToStr(desiredState) << "<-" << stateToStr(_state); + qCDebug(physics) << "CharacterController::setState" << stateToStr(desiredState) << "from" << stateToStr(_state) << "," << reason; #endif if (desiredState == State::Hover && _state != State::Hover) { // hover enter @@ -339,7 +346,7 @@ void CharacterController::setEnabled(bool enabled) { } _pendingFlags &= ~ PENDING_FLAG_ADD_TO_SIMULATION; } - setState(State::Hover); + SET_STATE(State::Hover, "setEnabled"); _enabled = enabled; } } @@ -438,8 +445,8 @@ void CharacterController::preSimulation() { const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 200 * MSECS_PER_SECOND; const btScalar MIN_HOVER_HEIGHT = 2.5f; const quint64 JUMP_TO_HOVER_PERIOD = 750 * MSECS_PER_SECOND; - const btScalar UPWARD_VELOCITY_THRESHOLD = 0.05f; - const btScalar MAX_FLYING_SPEED = 30.0f; + const btScalar UPWARD_VELOCITY_THRESHOLD = 0.1f; + const btScalar MAX_WALKING_SPEED = 2.5f; quint64 now = usecTimestampNow(); @@ -452,23 +459,22 @@ void CharacterController::preSimulation() { } bool jumpButtonHeld = _pendingFlags & PENDING_FLAG_JUMP; - bool wantsToGoUp = !jumpButtonHeld && _walkVelocity.dot(_currentUp) > UPWARD_VELOCITY_THRESHOLD; - bool tooFast = actualHorizVelocity.length() > (MAX_FLYING_SPEED / 2.0f); + bool flyingFast = _state == State::Hover && actualHorizVelocity.length() > (MAX_WALKING_SPEED * 0.75f); switch (_state) { case State::Ground: if (!rayCallback.hasHit() && !_hasSupport) { - setState(State::Hover); + SET_STATE(State::Hover, "no ground"); } else if (_pendingFlags & PENDING_FLAG_JUMP) { _takeOffJumpButtonID = _jumpButtonDownCount; - setState(State::Takeoff); + SET_STATE(State::Takeoff, "jump pressed"); } break; case State::Takeoff: if (!rayCallback.hasHit() && !_hasSupport) { - setState(State::Hover); + SET_STATE(State::Hover, "no ground"); } else if ((now - _takeoffToInAirStart) > TAKE_OFF_TO_IN_AIR_PERIOD) { - setState(State::InAir); + SET_STATE(State::InAir, "takeoff done"); _takeoffToInAirStart = now + USECS_PER_SECOND * 86500.0f; velocity += _jumpSpeed * _currentUp; _rigidBody->setLinearVelocity(velocity); @@ -476,17 +482,19 @@ void CharacterController::preSimulation() { break; case State::InAir: { if ((velocity.dot(_currentUp) <= (JUMP_SPEED / 2.0f)) && ((_floorDistance < JUMP_PROXIMITY_THRESHOLD) || _hasSupport)) { - setState(State::Ground); - } else if ((jumpButtonHeld && ((_takeOffJumpButtonID != _jumpButtonDownCount) || (now - _jumpButtonDownStart) > JUMP_TO_HOVER_PERIOD)) || wantsToGoUp) { - setState(State::Hover); + SET_STATE(State::Ground, "hit ground"); + } else if (jumpButtonHeld && (_takeOffJumpButtonID != _jumpButtonDownCount)) { + SET_STATE(State::Hover, "double jump button"); + } else if (jumpButtonHeld && (now - _jumpButtonDownStart) > JUMP_TO_HOVER_PERIOD) { + SET_STATE(State::Hover, "jump button held"); } break; } case State::Hover: - if (!jumpButtonHeld && !wantsToGoUp && _floorDistance < MIN_HOVER_HEIGHT && !tooFast) { - setState(State::InAir); - } else if (((_floorDistance < JUMP_PROXIMITY_THRESHOLD) || _hasSupport) && !tooFast) { - setState(State::Ground); + if ((_floorDistance < MIN_HOVER_HEIGHT) && !jumpButtonHeld && !flyingFast) { + SET_STATE(State::InAir, "near ground"); + } else if (((_floorDistance < JUMP_PROXIMITY_THRESHOLD) || _hasSupport) && !flyingFast) { + SET_STATE(State::Ground, "touching ground"); } break; } diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 97bda5c5d5..bb4a135ca3 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -31,6 +31,8 @@ class btRigidBody; class btCollisionWorld; class btDynamicsWorld; +//#define DEBUG_STATE_CHANGE + class CharacterController : public btCharacterControllerInterface { public: CharacterController(); @@ -92,7 +94,11 @@ public: bool getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation); protected: +#ifdef DEBUG_STATE_CHANGE + void setState(State state, const char* reason); +#else void setState(State state); +#endif void updateUpAxis(const glm::quat& rotation); bool checkForSupport(btCollisionWorld* collisionWorld) const; From 656608e2ffaff0f21effbe461b8f36437addd6d1 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 2 Feb 2016 12:08:52 -0800 Subject: [PATCH 08/15] Fixes for away.js --- .../meshes/defaultAvatar_full/avatar-animation.json | 1 + libraries/animation/src/Rig.cpp | 11 +++++++---- libraries/animation/src/Rig.h | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index 37da0c37cc..a4e39969b4 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -377,6 +377,7 @@ "interpTarget": 30, "interpDuration": 30, "transitions": [ + { "var": "isNotAway", "state": "awayOutro" }, { "var": "awayIntroOnDone", "state": "away"} ] }, diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 64935691c8..2ea9d782d5 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -784,11 +784,14 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos t += deltaTime; - if (_enableInverseKinematics) { - _animVars.set("ikOverlayAlpha", 1.0f); - } else { - _animVars.set("ikOverlayAlpha", 0.0f); + if (_enableInverseKinematics != _lastEnableInverseKinematics) { + if (_enableInverseKinematics) { + _animVars.set("ikOverlayAlpha", 1.0f); + } else { + _animVars.set("ikOverlayAlpha", 0.0f); + } } + _lastEnableInverseKinematics = _enableInverseKinematics; } _lastFront = front; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index e12af16e41..a360140b16 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -301,6 +301,7 @@ public: std::map _origRoleAnimations; std::vector _prefetchedAnimations; + bool _lastEnableInverseKinematics { false }; bool _enableInverseKinematics { true }; private: From cede14fdc6bfacbac590a504698342c250f19cd8 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 2 Feb 2016 14:01:44 -0800 Subject: [PATCH 09/15] Don't cut off title of windows without close icons --- interface/resources/qml/windows/DefaultFrame.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/windows/DefaultFrame.qml b/interface/resources/qml/windows/DefaultFrame.qml index a082db5a92..c58f9ca545 100644 --- a/interface/resources/qml/windows/DefaultFrame.qml +++ b/interface/resources/qml/windows/DefaultFrame.qml @@ -6,8 +6,10 @@ import "../controls" Frame { id: frame + property bool wideTopMargin: (window && (window.closable || window.title)); + Rectangle { - anchors { margins: -iconSize; topMargin: -iconSize * ((window && window.closable) ? 2 : 1); } + anchors { margins: -iconSize; topMargin: -iconSize * (wideTopMargin ? 2 : 1); } anchors.fill: parent; color: "#7f7f7f7f"; radius: 3; From 6cc3b2b47f82022ea00925887fa6ffbb9029e200 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 2 Feb 2016 15:04:28 -0800 Subject: [PATCH 10/15] Fixed unused variable warnings on OSX and Linux --- interface/src/avatar/MyAvatar.cpp | 1 - libraries/physics/src/CharacterController.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 4253091533..b99430cae3 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1314,7 +1314,6 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { const float RENDER_HEAD_CUTOFF_DISTANCE = 0.5f; bool MyAvatar::cameraInsideHead() const { - const Head* head = getHead(); const glm::vec3 cameraPosition = qApp->getCamera()->getPosition(); return glm::length(cameraPosition - getDefaultEyePosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getUniformScale()); } diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 160b227127..941428f03a 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -445,7 +445,6 @@ void CharacterController::preSimulation() { const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 200 * MSECS_PER_SECOND; const btScalar MIN_HOVER_HEIGHT = 2.5f; const quint64 JUMP_TO_HOVER_PERIOD = 750 * MSECS_PER_SECOND; - const btScalar UPWARD_VELOCITY_THRESHOLD = 0.1f; const btScalar MAX_WALKING_SPEED = 2.5f; quint64 now = usecTimestampNow(); From f24f2749e14b41e7a8f942c52ebaa36c36d0af72 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 2 Feb 2016 15:19:12 -0800 Subject: [PATCH 11/15] Make users.js show QML window for edit friends --- examples/users.js | 17 ++++++++++- interface/src/Application.cpp | 16 ---------- interface/src/Application.h | 2 -- .../GlobalServicesScriptingInterface.cpp | 3 -- .../GlobalServicesScriptingInterface.h | 3 +- libraries/ui/src/OffscreenUi.cpp | 29 ++++++++++--------- libraries/ui/src/QmlWebWindowClass.cpp | 5 +++- 7 files changed, 36 insertions(+), 39 deletions(-) diff --git a/examples/users.js b/examples/users.js index f63184625d..0f9be42835 100644 --- a/examples/users.js +++ b/examples/users.js @@ -235,7 +235,12 @@ var usersWindow = (function () { FRIENDS_BUTTON_HEIGHT = FRIENDS_BUTTON_SVG_HEIGHT, FRIENDS_BUTTON_COLOR = { red: 225, green: 225, blue: 225 }, FRIENDS_BUTTON_ALPHA = 0.95, + FRIENDS_WINDOW_URL = "https://metaverse.highfidelity.com/user/friends", + FRIENDS_WINDOW_WIDTH = 290, + FRIENDS_WINDOW_HEIGHT = 500, + FRIENDS_WINDOW_TITLE = "Add/Remove Friends", friendsButton, + friendsWindow, OPTION_BACKGROUND_COLOR = { red: 60, green: 60, blue: 60 }, OPTION_BACKGROUND_ALPHA = 0.1, @@ -643,7 +648,17 @@ var usersWindow = (function () { } if (clickedOverlay === friendsButton) { - GlobalServices.editFriends(); + if (!friendsWindow) { + friendsWindow = new OverlayWebWindow({ + title: FRIENDS_WINDOW_TITLE, + width: FRIENDS_WINDOW_WIDTH, + height: FRIENDS_WINDOW_HEIGHT, + visible: false + }); + } + friendsWindow.setURL(FRIENDS_WINDOW_URL); + friendsWindow.setVisible(true); + friendsWindow.raise(); } } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 883b082d0e..d05440b2e6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4615,22 +4615,6 @@ void Application::activeChanged(Qt::ApplicationState state) { break; } } -void Application::showFriendsWindow() { - const QString FRIENDS_WINDOW_OBJECT_NAME = "FriendsWindow"; - const QString FRIENDS_WINDOW_TITLE = "Add/Remove Friends"; - const QString FRIENDS_WINDOW_URL = "https://metaverse.highfidelity.com/user/friends"; - const int FRIENDS_WINDOW_WIDTH = 290; - const int FRIENDS_WINDOW_HEIGHT = 500; - auto webWindowClass = _window->findChildren(FRIENDS_WINDOW_OBJECT_NAME); - if (webWindowClass.empty()) { - auto friendsWindow = new WebWindowClass(FRIENDS_WINDOW_TITLE, FRIENDS_WINDOW_URL, FRIENDS_WINDOW_WIDTH, - FRIENDS_WINDOW_HEIGHT); - friendsWindow->setParent(_window); - friendsWindow->setObjectName(FRIENDS_WINDOW_OBJECT_NAME); - connect(friendsWindow, &WebWindowClass::closed, &WebWindowClass::deleteLater); - friendsWindow->setVisible(true); - } -} void Application::postLambdaEvent(std::function f) { if (this->thread() == QThread::currentThread()) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 4b32d8879b..ef8a5377fd 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -245,8 +245,6 @@ public slots: void handleLocalServerConnection(); void readArgumentsFromLocalSocket(); - void showFriendsWindow(); - void packageModel(); void openUrl(const QUrl& url); diff --git a/interface/src/scripting/GlobalServicesScriptingInterface.cpp b/interface/src/scripting/GlobalServicesScriptingInterface.cpp index e764473107..7dac0247bd 100644 --- a/interface/src/scripting/GlobalServicesScriptingInterface.cpp +++ b/interface/src/scripting/GlobalServicesScriptingInterface.cpp @@ -144,6 +144,3 @@ void GlobalServicesScriptingInterface::updateDownloadInfo() { emit downloadInfoChanged(getDownloadInfo()); } -void GlobalServicesScriptingInterface::editFriends() { - QMetaObject::invokeMethod(qApp, "showFriendsWindow"); -} diff --git a/interface/src/scripting/GlobalServicesScriptingInterface.h b/interface/src/scripting/GlobalServicesScriptingInterface.h index e38bfc0eb6..11d8735187 100644 --- a/interface/src/scripting/GlobalServicesScriptingInterface.h +++ b/interface/src/scripting/GlobalServicesScriptingInterface.h @@ -45,8 +45,7 @@ public: public slots: DownloadInfoResult getDownloadInfo(); void updateDownloadInfo(); - void editFriends(); - + private slots: void loggedOut(); void checkDownloadInfo(); diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 8e52507243..1d471d5419 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -45,6 +45,20 @@ private: bool _navigationFocused { false }; }; +QString fixupHifiUrl(const QString& urlString) { + static const QString ACCESS_TOKEN_PARAMETER = "access_token"; + static const QString ALLOWED_HOST = "metaverse.highfidelity.com"; + QUrl url(urlString); + QUrlQuery query(url); + if (url.host() == ALLOWED_HOST && query.allQueryItemValues(ACCESS_TOKEN_PARAMETER).empty()) { + AccountManager& accountManager = AccountManager::getInstance(); + query.addQueryItem(ACCESS_TOKEN_PARAMETER, accountManager.getAccountInfo().getAccessToken().token); + url.setQuery(query.query()); + return url.toString(); + } + return urlString; +} + class UrlHandler : public QObject { Q_OBJECT public: @@ -60,20 +74,7 @@ public: // FIXME hack for authentication, remove when we migrate to Qt 5.6 Q_INVOKABLE QString fixupUrl(const QString& originalUrl) { - static const QString ACCESS_TOKEN_PARAMETER = "access_token"; - static const QString ALLOWED_HOST = "metaverse.highfidelity.com"; - QString result = originalUrl; - QUrl url(originalUrl); - QUrlQuery query(url); - if (url.host() == ALLOWED_HOST && query.allQueryItemValues(ACCESS_TOKEN_PARAMETER).empty()) { - qDebug() << "Updating URL with auth token"; - AccountManager& accountManager = AccountManager::getInstance(); - query.addQueryItem(ACCESS_TOKEN_PARAMETER, accountManager.getAccountInfo().getAccessToken().token); - url.setQuery(query.query()); - result = url.toString(); - } - - return result; + return fixupHifiUrl(originalUrl); } }; diff --git a/libraries/ui/src/QmlWebWindowClass.cpp b/libraries/ui/src/QmlWebWindowClass.cpp index 96e99654c8..f12fb51b19 100644 --- a/libraries/ui/src/QmlWebWindowClass.cpp +++ b/libraries/ui/src/QmlWebWindowClass.cpp @@ -49,8 +49,11 @@ QString QmlWebWindowClass::getURL() const { return result.toString(); } +// HACK find a good place to declare and store this +extern QString fixupHifiUrl(const QString& urlString); + void QmlWebWindowClass::setURL(const QString& urlString) { DependencyManager::get()->executeOnUiThread([=] { - _qmlWindow->setProperty(URL_PROPERTY, urlString); + _qmlWindow->setProperty(URL_PROPERTY, fixupHifiUrl(urlString)); }); } From bc7fda0ae9c69f6153d1fc6b56a19009a01fe440 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 2 Feb 2016 15:19:47 -0800 Subject: [PATCH 12/15] Set desirable global settings for QML web views --- interface/resources/qml/hifi/Desktop.qml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml index 9d527c697a..5951101194 100644 --- a/interface/resources/qml/hifi/Desktop.qml +++ b/interface/resources/qml/hifi/Desktop.qml @@ -1,5 +1,6 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 +import QtWebEngine 1.1; import "../desktop" import ".." @@ -7,6 +8,13 @@ import ".." Desktop { id: desktop + Component.onCompleted: { + WebEngine.settings.javascriptCanOpenWindows = false; + WebEngine.settings.javascriptCanAccessClipboard = false; + WebEngine.settings.spatialNavigationEnabled = true; + WebEngine.settings.localContentCanAccessRemoteUrls = true; + } + // The tool window, one instance property alias toolWindow: toolWindow ToolWindow { id: toolWindow } From 30e97c2a76bcdafa30d2f2cef9866917de680fc6 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 2 Feb 2016 16:34:06 -0800 Subject: [PATCH 13/15] run attachedEntitiesManager.js by default, but without a UI --- examples/attachedEntitiesManager.js | 75 ++++++++++++++++------------- examples/defaultScripts.js | 1 + 2 files changed, 42 insertions(+), 34 deletions(-) diff --git a/examples/attachedEntitiesManager.js b/examples/attachedEntitiesManager.js index 01f8f861c9..c44ac66a44 100644 --- a/examples/attachedEntitiesManager.js +++ b/examples/attachedEntitiesManager.js @@ -22,41 +22,44 @@ var MINIMUM_DROP_DISTANCE_FROM_JOINT = 0.4; var ATTACHED_ENTITY_SEARCH_DISTANCE = 10.0; var ATTACHED_ENTITIES_SETTINGS_KEY = "ATTACHED_ENTITIES"; var DRESSING_ROOM_DISTANCE = 2.0; +var SHOW_TOOL_BAR = false; // tool bar -HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; -var BUTTON_SIZE = 32; -var PADDING = 3; -Script.include(["libraries/toolBars.js"]); -var toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.attachedEntities.toolbar", function(screenSize) { - return { - x: (BUTTON_SIZE + PADDING), - y: (screenSize.y / 2 - BUTTON_SIZE * 2 + PADDING) - }; -}); -var saveButton = toolBar.addOverlay("image", { - width: BUTTON_SIZE, - height: BUTTON_SIZE, - imageURL: "http://headache.hungry.com/~seth/hifi/save.png", - color: { - red: 255, - green: 255, - blue: 255 - }, - alpha: 1 -}); -var loadButton = toolBar.addOverlay("image", { - width: BUTTON_SIZE, - height: BUTTON_SIZE, - imageURL: "http://headache.hungry.com/~seth/hifi/load.png", - color: { - red: 255, - green: 255, - blue: 255 - }, - alpha: 1 -}); +if (SHOW_TOOL_BAR) { + HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; + var BUTTON_SIZE = 32; + var PADDING = 3; + Script.include(["libraries/toolBars.js"]); + var toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.attachedEntities.toolbar", function(screenSize) { + return { + x: (BUTTON_SIZE + PADDING), + y: (screenSize.y / 2 - BUTTON_SIZE * 2 + PADDING) + }; + }); + var saveButton = toolBar.addOverlay("image", { + width: BUTTON_SIZE, + height: BUTTON_SIZE, + imageURL: "http://headache.hungry.com/~seth/hifi/save.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1 + }); + var loadButton = toolBar.addOverlay("image", { + width: BUTTON_SIZE, + height: BUTTON_SIZE, + imageURL: "http://headache.hungry.com/~seth/hifi/load.png", + color: { + red: 255, + green: 255, + blue: 255 + }, + alpha: 1 + }); +} function mousePressEvent(event) { @@ -73,10 +76,14 @@ function mousePressEvent(event) { } function scriptEnding() { - toolBar.cleanup(); + if (SHOW_TOOL_BAR) { + toolBar.cleanup(); + } } -Controller.mousePressEvent.connect(mousePressEvent); +if (SHOW_TOOL_BAR) { + Controller.mousePressEvent.connect(mousePressEvent); +} Script.scriptEnding.connect(scriptEnding); diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js index 5ca62470ee..35af5f4eae 100644 --- a/examples/defaultScripts.js +++ b/examples/defaultScripts.js @@ -21,3 +21,4 @@ Script.load("controllers/squeezeHands.js"); Script.load("grab.js"); Script.load("directory.js"); Script.load("dialTone.js"); +Script.load("attachedEntitiesManager.js"); From fff603e4e2f5a48567f7fb390cebdcb0c07a019d Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 3 Feb 2016 10:21:27 -0800 Subject: [PATCH 14/15] Removed comment --- libraries/physics/src/CharacterController.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index 941428f03a..d16c406658 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -499,8 +499,6 @@ void CharacterController::preSimulation() { } } - /// _walkVelocity.dot(_currentUp) > UPWARD_VELOCITY_THRESHOLD - _previousFlags = _pendingFlags; _pendingFlags &= ~PENDING_FLAG_JUMP; From ad2a7bfc3c4048bae461a0de08244ab0108c92fc Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 3 Feb 2016 10:54:08 -0800 Subject: [PATCH 15/15] code review --- examples/attachedEntitiesManager.js | 4 ++-- examples/controllers/handControllerGrab.js | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/attachedEntitiesManager.js b/examples/attachedEntitiesManager.js index c44ac66a44..1cdd61ad39 100644 --- a/examples/attachedEntitiesManager.js +++ b/examples/attachedEntitiesManager.js @@ -40,7 +40,7 @@ if (SHOW_TOOL_BAR) { var saveButton = toolBar.addOverlay("image", { width: BUTTON_SIZE, height: BUTTON_SIZE, - imageURL: "http://headache.hungry.com/~seth/hifi/save.png", + imageURL: ".../save.png", color: { red: 255, green: 255, @@ -51,7 +51,7 @@ if (SHOW_TOOL_BAR) { var loadButton = toolBar.addOverlay("image", { width: BUTTON_SIZE, height: BUTTON_SIZE, - imageURL: "http://headache.hungry.com/~seth/hifi/load.png", + imageURL: ".../load.png", color: { red: 255, green: 255, diff --git a/examples/controllers/handControllerGrab.js b/examples/controllers/handControllerGrab.js index 842c54b86d..ee3184d78f 100644 --- a/examples/controllers/handControllerGrab.js +++ b/examples/controllers/handControllerGrab.js @@ -1224,6 +1224,7 @@ function MyController(hand) { return true; } } + return false; } this.getPresetPosition = function() {