From e8b5b56df75fa78bccca20414392b8a737722714 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 5 Dec 2016 15:16:49 -0800 Subject: [PATCH 1/4] Revert "Merge pull request #9151 from hyperlogic/bug-fix/reset-sensor-in-desktop-mode" This reverts commit 119653bceb616479ed5499077cd119928a2a4b20, reversing changes made to a69d77ffbe4f31edbc720515f0f37af459e6f134. --- interface/src/Application.cpp | 4 ++-- interface/src/Application.h | 2 +- interface/src/avatar/MyAvatar.cpp | 6 +----- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a2eb58def4..900d1bbb63 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4733,13 +4733,13 @@ void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& regi renderArgs->_viewport = originalViewport; } -void Application::resetSensors(bool andRecenter) { +void Application::resetSensors(bool andReload) { DependencyManager::get()->reset(); DependencyManager::get()->reset(); DependencyManager::get()->reset(); getActiveDisplayPlugin()->resetSensors(); _overlayConductor.centerUI(); - getMyAvatar()->reset(andRecenter); + getMyAvatar()->reset(andReload); QMetaObject::invokeMethod(DependencyManager::get().data(), "reset", Qt::QueuedConnection); } diff --git a/interface/src/Application.h b/interface/src/Application.h index a57fa1b02d..5ab94465cc 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -317,7 +317,7 @@ public slots: void openUrl(const QUrl& url) const; - void resetSensors(bool andRecenter = false); + void resetSensors(bool andReload = false); void setActiveFaceTracker() const; #if (PR_BUILD || DEV_BUILD) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 4296907863..5b691a6dc6 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -254,10 +254,6 @@ void MyAvatar::centerBody() { return; } - if (!qApp->isHMDMode()) { - return; - } - // derive the desired body orientation from the current hmd orientation, before the sensor reset. auto newBodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. @@ -312,7 +308,7 @@ void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { } setThrust(glm::vec3(0.0f)); - if (andRecenter && qApp->isHMDMode()) { + if (andRecenter) { // derive the desired body orientation from the *old* hmd orientation, before the sensor reset. auto newBodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. From 49ce04e214db275aa203b3d6cc8da1cee068491d Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 5 Dec 2016 15:17:32 -0800 Subject: [PATCH 2/4] Revert "Merge pull request #9150 from hyperlogic/bug-fix/driving-while-flying-in-hmd" This reverts commit a69d77ffbe4f31edbc720515f0f37af459e6f134, reversing changes made to 996d3f58754f1203c157507cd4e5d5ade1ab4f54. --- interface/src/avatar/MyAvatar.cpp | 6 +----- interface/src/avatar/MyAvatar.h | 3 +-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5b691a6dc6..9a0b04aa6d 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -467,8 +467,6 @@ void MyAvatar::simulate(float deltaTime) { if (_characterController.getState() != CharacterController::State::Hover) { updateSensorToWorldMatrix(_enableVerticalComfortMode ? SensorToWorldUpdateMode::VerticalComfort : SensorToWorldUpdateMode::Vertical); - } else { - updateSensorToWorldMatrix(SensorToWorldUpdateMode::None); } { @@ -618,8 +616,6 @@ void MyAvatar::updateSensorToWorldMatrix(SensorToWorldUpdateMode mode) { } else if (mode == SensorToWorldUpdateMode::Vertical) { setSensorToWorldMatrix(sensorToWorldMat); } - } else if (mode == SensorToWorldUpdateMode::None) { - setSensorToWorldMatrix(_sensorToWorldMatrix); } } @@ -1901,7 +1897,7 @@ void MyAvatar::applyVelocityToSensorToWorldMatrix(const glm::vec3& velocity, flo // update the position column of matrix glm::mat4 newSensorToWorldMatrix = _sensorToWorldMatrix; newSensorToWorldMatrix[3] = glm::vec4(position, 1.0f); - _sensorToWorldMatrix = newSensorToWorldMatrix; + setSensorToWorldMatrix(newSensorToWorldMatrix); } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 72936ec0ce..feef171a29 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -192,8 +192,7 @@ public: enum class SensorToWorldUpdateMode { Full = 0, Vertical, - VerticalComfort, - None + VerticalComfort }; void updateSensorToWorldMatrix(SensorToWorldUpdateMode mode = SensorToWorldUpdateMode::Full); From 0c8105b5519753bb177df062d5d1f509a56d7c33 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 5 Dec 2016 15:17:53 -0800 Subject: [PATCH 3/4] Revert "Merge pull request #9141 from hyperlogic/feature/my-avatar-docs-part-1" This reverts commit 934148fea50b6564a5c8a10975e9d4abea25cda9, reversing changes made to d2b32c3084700d67f9edcdef19a60d386339be12. --- interface/src/avatar/MyAvatar.h | 157 +------------------ libraries/controllers/src/controllers/Pose.h | 10 -- tools/jsdoc/plugins/hifi.js | 9 +- 3 files changed, 8 insertions(+), 168 deletions(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index feef171a29..a5dc01b12b 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -88,57 +88,6 @@ class MyAvatar : public Avatar { Q_PROPERTY(bool hmdLeanRecenterEnabled READ getHMDLeanRecenterEnabled WRITE setHMDLeanRecenterEnabled) Q_PROPERTY(bool avatarCollisionsEnabled READ getAvatarCollisionsEnabled WRITE setAvatarCollisionsEnabled) - /**jsdoc - * Your avatar is your in-world representation of you. The MyAvatar API is used to manipulate the avatar. - * For example, using the MyAvatar API you can customize the avatar's appearance, run custom avatar animations, - * change the avatar's position within the domain, or manage the avatar's collisions with other objects. - * NOTE: MyAvatar extends Avatar and AvatarData, see those namespace for more properties/methods. - * - * @namespace MyAvatar - * @augments Avatar - * @property shouldRenderLocally {bool} Set it to true if you would like to see MyAvatar in your local interface, - * and false if you would not like to see MyAvatar in your local interface. - * @property motorVelocity {Vec3} Can be used to move the avatar with this velocity. - * @property motorTimescale {float} Specifies how quickly the avatar should accelerate to meet the motorVelocity, - * smaller values will result in higher acceleration. - * @property motorReferenceFrame {string} Reference frame of the motorVelocity, must be one of the following: "avatar", "camera", "world" - * @property collisionSoundURL {string} Specifies the sound to play when the avatar experiences a collision. - * You can provide a mono or stereo 16-bit WAV file running at either 24 Khz or 48 Khz. - * The latter is downsampled by the audio mixer, so all audio effectively plays back at a 24 Khz sample rate. - * 48 Khz RAW files are also supported. - * @property audioListenerMode {number} When hearing spatialized audio this determines where the listener placed. - * Should be one of the following values: - * MyAvatar.audioListenerModeHead - the listener located at the avatar's head. - * MyAvatar.audioListenerModeCamera - the listener is relative to the camera. - * MyAvatar.audioListenerModeCustom - the listener is at a custom location specified by the MyAvatar.customListenPosition - * and MyAvatar.customListenOrientation properties. - * @property customListenPosition {Vec3} If MyAvatar.audioListenerMode == MyAvatar.audioListenerModeHead, then this determines the position - * of audio spatialization listener. - * @property customListenOreintation {Quat} If MyAvatar.audioListenerMode == MyAvatar.audioListenerModeHead, then this determines the orientation - * of the audio spatialization listener. - * @property audioListenerModeHead {number} READ-ONLY. When passed to MyAvatar.audioListenerMode, it will set the audio listener - * around the avatar's head. - * @property audioListenerModeCamera {number} READ-ONLY. When passed to MyAvatar.audioListenerMode, it will set the audio listener - * around the camera. - * @property audioListenerModeCustom {number} READ-ONLY. When passed to MyAvatar.audioListenerMode, it will set the audio listener - * around the value specified by MyAvatar.customListenPosition and MyAvatar.customListenOrientation. - * @property leftHandPosition {Vec3} READ-ONLY. The desired position of the left wrist in avatar space, determined by the hand controllers. - * Note: only valid if hand controllers are in use. - * @property rightHandPosition {Vec3} READ-ONLY. The desired position of the right wrist in avatar space, determined by the hand controllers. - * Note: only valid if hand controllers are in use. - * @property leftHandTipPosition {Vec3} READ-ONLY. A position 30 cm offset from MyAvatar.leftHandPosition - * @property rightHandTipPosition {Vec3} READ-ONLY. A position 30 cm offset from MyAvatar.rightHandPosition - * @property leftHandPose {Pose} READ-ONLY. Returns full pose (translation, orientation, velocity & angularVelocity) of the desired - * wrist position, determined by the hand controllers. - * @property rightHandPose {Pose} READ-ONLY. Returns full pose (translation, orientation, velocity & angularVelocity) of the desired - * wrist position, determined by the hand controllers. - * @property leftHandTipPose {Pose} READ-ONLY. Returns a pose offset 30 cm from MyAvatar.leftHandPose - * @property rightHandTipPose {Pose} READ-ONLY. Returns a pose offset 30 cm from MyAvatar.rightHandPose - * @property hmdLeanRecenterEnabled {bool} This can be used disable the hmd lean recenter behavior. This behavior is what causes your avatar - * to follow your HMD as you walk around the room, in room scale VR. Disabling this is useful if you desire to pin the avatar to a fixed location. - * @property avatarCollisionsEnabled {bool} This can be used to disable collisions between the avatar and the world. - */ - public: explicit MyAvatar(RigPointer rig); ~MyAvatar(); @@ -151,17 +100,7 @@ public: void reset(bool andRecenter = false, bool andReload = true, bool andHead = true); - /**jsdoc - * Moves and orients the avatar, such that it is directly underneath the HMD, with toes pointed forward. - * @function MyAvatar.centerBody - */ Q_INVOKABLE void centerBody(); // thread-safe - - /**jsdoc - * The internal inverse-kinematics system maintains a record of which joints are "locked". Sometimes it is useful to forget this history, to prevent - * contorted joints. - * @function MyAvatar.centerBody - */ Q_INVOKABLE void clearIKJointLimitHistory(); // thread-safe void update(float deltaTime); @@ -200,109 +139,23 @@ public: void setRealWorldFieldOfView(float realWorldFov) { _realWorldFieldOfView.set(realWorldFov); } - /**jsdoc - * The default position in world coordinates of the point directly between the avatar's eyes - * @function MyAvatar.getDefaultEyePosition - * @example This example gets the default eye position and prints it to the debug log. - * var defaultEyePosition = MyAvatar.getDefaultEyePosition(); - * print (JSON.stringify(defaultEyePosition)); - * @returns {Vec3} Position between the avatar's eyes. - */ Q_INVOKABLE glm::vec3 getDefaultEyePosition() const; float getRealWorldFieldOfView() { return _realWorldFieldOfView.get(); } - /**jsdoc - * The avatar animation system includes a set of default animations along with rules for how those animations are blended - * together with procedural data (such as look at vectors, hand sensors etc.). overrideAnimation() is used to completely - * override all motion from the default animation system (including inverse kinematics for hand and head controllers) and - * play a specified animation. To end this animation and restore the default animations, use MyAvatar.restoreAnimation. - * @function MyAvatar.overrideAnimation - * @example Play a clapping animation on your avatar for three seconds. - * // Clap your hands for 3 seconds then restore animation back to the avatar. - * var ANIM_URL = "https://s3.amazonaws.com/hifi-public/animations/ClapAnimations/ClapHands_Standing.fbx"; - * MyAvatar.overrideAnimation(ANIM_URL, 30, true, 0, 53); - * Script.setTimeout(function () { - * MyAvatar.restoreAnimation(); - * }, 3000); - * @param url {string} The URL to the animation file. Animation files need to be .FBX format, but only need to contain the avatar skeleton and animation data. - * @param fps {number} The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed. - * @param loop {bool} Set to true if the animation should loop. - * @param firstFrame {number} The frame the animation should start at. - * @param lastFrame {number} The frame the animation should end at. - */ + // Interrupt the current animation with a custom animation. Q_INVOKABLE void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); - /**jsdoc - * The avatar animation system includes a set of default animations along with rules for how those animations are blended together with - * procedural data (such as look at vectors, hand sensors etc.). Playing your own custom animations will override the default animations. - * restoreAnimation() is used to restore all motion from the default animation system including inverse kinematics for hand and head - * controllers. If you aren't currently playing an override animation, this function will have no effect. - * @function MyAvatar.restoreAnimation - * @example Play a clapping animation on your avatar for three seconds. - * // Clap your hands for 3 seconds then restore animation back to the avatar. - * var ANIM_URL = "https://s3.amazonaws.com/hifi-public/animations/ClapAnimations/ClapHands_Standing.fbx"; - * MyAvatar.overrideAnimation(ANIM_URL, 30, true, 0, 53); - * Script.setTimeout(function () { - * MyAvatar.restoreAnimation(); - * }, 3000); - */ + // Stop the animation that was started with overrideAnimation and go back to the standard animation. Q_INVOKABLE void restoreAnimation(); - /**jsdoc - * Each avatar has an avatar-animation.json file that defines which animations are used and how they are blended together with procedural data - * (such as look at vectors, hand sensors etc.). Each animation specified in the avatar-animation.json file is known as an animation role. - * Animation roles map to easily understandable actions that the avatar can perform, such as "idleStand", "idleTalk", or "walkFwd." - * getAnimationRoles() is used get the list of animation roles defined in the avatar-animation.json. - * @function MyAvatar.getAnimatationRoles - * @example This example prints the list of animation roles defined in the avatar's avatar-animation.json file to the debug log. - * var roles = MyAvatar.getAnimationRoles(); - * print("Animation Roles:"); - * for (var i = 0; i < roles.length; i++) { - * print(roles[i]); - * } - * @returns {string[]} Array of role strings - */ + // Returns a list of all clips that are available Q_INVOKABLE QStringList getAnimationRoles(); - /**jsdoc - * Each avatar has an avatar-animation.json file that defines a set of animation roles. Animation roles map to easily understandable actions - * that the avatar can perform, such as "idleStand", "idleTalk", or "walkFwd". To get the full list of roles, use getAnimationRoles(). - * For each role, the avatar-animation.json defines when the animation is used, the animation clip (.FBX) used, and how animations are blended - * together with procedural data (such as look at vectors, hand sensors etc.). - * overrideRoleAnimation() is used to change the animation clip (.FBX) associated with a specified animation role. - * Note: Hand roles only affect the hand. Other 'main' roles, like 'idleStand', 'idleTalk', 'takeoffStand' are full body. - * @function MyAvatar.overrideRoleAnimation - * @example The default avatar-animation.json defines an "idleStand" animation role. This role specifies that when the avatar is not moving, - * an animation clip of the avatar idling with hands hanging at its side will be used. It also specifies that when the avatar moves, the animation - * will smoothly blend to the walking animation used by the "walkFwd" animation role. - * In this example, the "idleStand" role animation clip has been replaced with a clapping animation clip. Now instead of standing with its arms - * hanging at its sides when it is not moving, the avatar will stand and clap its hands. Note that just as it did before, as soon as the avatar - * starts to move, the animation will smoothly blend into the walk animation used by the "walkFwd" animation role. - * // An animation of the avatar clapping its hands while standing - * var ANIM_URL = "https://s3.amazonaws.com/hifi-public/animations/ClapAnimations/ClapHands_Standing.fbx"; - * MyAvatar.overrideRoleAnimation("idleStand", ANIM_URL, 30, true, 0, 53); - * // To restore the default animation, use MyAvatar.restoreRoleAnimation(). - * @param role {string} The animation role to override - * @param url {string} The URL to the animation file. Animation files need to be .FBX format, but only need to contain the avatar skeleton and animation data. - * @param fps {number} The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed. - * @param loop {bool} Set to true if the animation should loop - * @param firstFrame {number} The frame the animation should start at - * @param lastFrame {number} The frame the animation should end at - */ + // Replace an existing standard role animation with a custom one. Q_INVOKABLE void overrideRoleAnimation(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame); - /**jsdoc - * Each avatar has an avatar-animation.json file that defines a set of animation roles. Animation roles map to easily understandable actions that - * the avatar can perform, such as "idleStand", "idleTalk", or "walkFwd". To get the full list of roles, use getAnimationRoles(). For each role, - * the avatar-animation.json defines when the animation is used, the animation clip (.FBX) used, and how animations are blended together with - * procedural data (such as look at vectors, hand sensors etc.). You can change the animation clip (.FBX) associated with a specified animation - * role using overrideRoleAnimation(). - * restoreRoleAnimation() is used to restore a specified animation role's default animation clip. If you have not specified an override animation - * for the specified role, this function will have no effect. - * @function MyAvatar.restoreRoleAnimation - * @param rule {string} The animation role clip to restore - */ + // remove an animation role override and return to the standard animation. Q_INVOKABLE void restoreRoleAnimation(const QString& role); // Adds handler(animStateDictionaryIn) => animStateDictionaryOut, which will be invoked just before each animGraph state update. diff --git a/libraries/controllers/src/controllers/Pose.h b/libraries/controllers/src/controllers/Pose.h index c16b3ae34b..47ba59279a 100644 --- a/libraries/controllers/src/controllers/Pose.h +++ b/libraries/controllers/src/controllers/Pose.h @@ -42,16 +42,6 @@ namespace controller { Pose transform(const glm::mat4& mat) const; - /**jsdoc - * Represents a hand controller pose typically received from Controller.getPoseValue - * Unless otherwise noted all properties are in avatar space. - * - * @typedef Pose - * @property translation {Vec3} position of controller - * @property rotation {Quat} orientation of controller - * @property velocity {Vec3} current velocity of controller (meters/sec) - * @property angularVelocity {Vec3} current angular velocity of controller (radians/sec) - */ static QScriptValue toScriptValue(QScriptEngine* engine, const Pose& event); static void fromScriptValue(const QScriptValue& object, Pose& event); }; diff --git a/tools/jsdoc/plugins/hifi.js b/tools/jsdoc/plugins/hifi.js index c3fa14025f..8a6d2bf0f2 100644 --- a/tools/jsdoc/plugins/hifi.js +++ b/tools/jsdoc/plugins/hifi.js @@ -15,15 +15,12 @@ exports.handlers = { // directories to scan for jsdoc comments var dirList = [ '../../interface/src', - '../../interface/src/avatar', '../../interface/src/scripting', '../../interface/src/ui/overlays', - '../../libraries/animation/src', - '../../libraries/avatars/src', - '../../libraries/controllers/src/controllers/', - '../../libraries/entities/src', + '../../libraries/script-engine/src', '../../libraries/networking/src', - '../../libraries/script-engine/src' + '../../libraries/animation/src', + '../../libraries/entities/src', ]; var exts = ['.h', '.cpp']; From 89ae3b3d6efda0d47f2b6d55fcc530d1a076bea5 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 5 Dec 2016 15:18:03 -0800 Subject: [PATCH 4/4] Revert "Merge pull request #8691 from highfidelity/out-of-body-experience" This reverts commit efe9571ab82b3b70877b0140c70a37307c5a39dd, reversing changes made to 333e9ec7f4111a9fe4b0d6b7a6a80b56b8ca00fd. --- .../resources/avatar/avatar-animation.json | 45 +- interface/src/Application.cpp | 9 +- interface/src/Menu.cpp | 16 +- interface/src/Menu.h | 9 +- interface/src/avatar/MyAvatar.cpp | 465 ++++---------- interface/src/avatar/MyAvatar.h | 65 +- .../src/avatar/MyCharacterController.cpp | 443 +------------ interface/src/avatar/MyCharacterController.h | 29 +- interface/src/avatar/SkeletonModel.cpp | 67 +- .../src/scripting/HMDScriptingInterface.cpp | 20 - .../src/scripting/HMDScriptingInterface.h | 12 +- libraries/animation/src/AnimBlendLinear.cpp | 14 +- libraries/animation/src/AnimBlendLinear.h | 4 +- .../animation/src/AnimBlendLinearMove.cpp | 14 +- libraries/animation/src/AnimBlendLinearMove.h | 4 +- libraries/animation/src/AnimClip.cpp | 2 +- libraries/animation/src/AnimClip.h | 2 +- libraries/animation/src/AnimContext.cpp | 16 - libraries/animation/src/AnimContext.h | 30 - .../animation/src/AnimInverseKinematics.cpp | 79 +-- .../animation/src/AnimInverseKinematics.h | 9 +- libraries/animation/src/AnimManipulator.cpp | 6 +- libraries/animation/src/AnimManipulator.h | 4 +- libraries/animation/src/AnimNode.h | 8 +- libraries/animation/src/AnimOverlay.cpp | 6 +- libraries/animation/src/AnimOverlay.h | 2 +- libraries/animation/src/AnimStateMachine.cpp | 14 +- libraries/animation/src/AnimStateMachine.h | 4 +- libraries/animation/src/Rig.cpp | 85 +-- libraries/animation/src/Rig.h | 10 - libraries/physics/src/BulletUtil.h | 2 +- libraries/physics/src/CharacterController.cpp | 600 ++++++------------ libraries/physics/src/CharacterController.h | 79 +-- .../physics/src/CharacterGhostObject.cpp | 415 ------------ libraries/physics/src/CharacterGhostObject.h | 103 --- libraries/physics/src/CharacterGhostShape.cpp | 31 - libraries/physics/src/CharacterGhostShape.h | 25 - libraries/physics/src/CharacterRayResult.cpp | 31 - libraries/physics/src/CharacterRayResult.h | 44 -- .../physics/src/CharacterSweepResult.cpp | 42 -- libraries/physics/src/CharacterSweepResult.h | 45 -- .../physics/src/CollisionRenderMeshCache.cpp | 2 +- .../physics/src/CollisionRenderMeshCache.h | 2 +- libraries/physics/src/ContactInfo.cpp | 2 +- libraries/physics/src/ContactInfo.h | 2 +- libraries/physics/src/ObjectAction.cpp | 2 +- libraries/physics/src/ObjectAction.h | 2 +- libraries/physics/src/ObjectMotionState.cpp | 2 +- libraries/physics/src/ObjectMotionState.h | 2 +- .../physics/src/PhysicalEntitySimulation.cpp | 2 +- .../physics/src/PhysicalEntitySimulation.h | 2 +- libraries/physics/src/PhysicsEngine.cpp | 2 +- libraries/physics/src/PhysicsEngine.h | 2 +- libraries/physics/src/ShapeFactory.cpp | 2 +- libraries/physics/src/ShapeFactory.h | 2 +- libraries/physics/src/ShapeManager.cpp | 2 +- libraries/physics/src/ShapeManager.h | 2 +- libraries/shared/src/BackgroundMode.h | 2 +- libraries/shared/src/GeometryUtil.cpp | 39 -- libraries/shared/src/GeometryUtil.h | 5 - libraries/shared/src/ShapeInfo.cpp | 2 +- libraries/shared/src/ShapeInfo.h | 2 +- scripts/system/controllers/teleport.js | 80 +-- 63 files changed, 549 insertions(+), 2523 deletions(-) mode change 100755 => 100644 interface/src/avatar/MyAvatar.cpp mode change 100755 => 100644 interface/src/avatar/MyCharacterController.cpp delete mode 100644 libraries/animation/src/AnimContext.cpp delete mode 100644 libraries/animation/src/AnimContext.h mode change 100755 => 100644 libraries/physics/src/CharacterController.cpp delete mode 100755 libraries/physics/src/CharacterGhostObject.cpp delete mode 100755 libraries/physics/src/CharacterGhostObject.h delete mode 100644 libraries/physics/src/CharacterGhostShape.cpp delete mode 100644 libraries/physics/src/CharacterGhostShape.h delete mode 100755 libraries/physics/src/CharacterRayResult.cpp delete mode 100644 libraries/physics/src/CharacterRayResult.h delete mode 100755 libraries/physics/src/CharacterSweepResult.cpp delete mode 100644 libraries/physics/src/CharacterSweepResult.h diff --git a/interface/resources/avatar/avatar-animation.json b/interface/resources/avatar/avatar-animation.json index ba4c66f045..834a3fc277 100644 --- a/interface/resources/avatar/avatar-animation.json +++ b/interface/resources/avatar/avatar-animation.json @@ -255,10 +255,10 @@ }, { "id": "idleToWalkFwd", - "interpTarget": 4, - "interpDuration": 3.0, + "interpTarget": 3, + "interpDuration": 3, "transitions": [ - { "var": "idleToWalkFwdClipOnDone", "state": "walkFwd" }, + { "var": "idleToWalkFwdOnDone", "state": "walkFwd" }, { "var": "isNotMoving", "state": "idle" }, { "var": "isMovingBackward", "state": "walkBwd" }, { "var": "isMovingRight", "state": "strafeRight" }, @@ -292,8 +292,8 @@ }, { "id": "walkBwd", - "interpTarget": 4, - "interpDuration": 4, + "interpTarget": 6, + "interpDuration": 6, "transitions": [ { "var": "isNotMoving", "state": "idle" }, { "var": "isMovingForward", "state": "walkFwd" }, @@ -523,7 +523,7 @@ "data": { "alpha": 0.0, "desiredSpeed": 1.4, - "characteristicSpeeds": [0.5, 1.3, 4.5], + "characteristicSpeeds": [0.5, 1.4, 4.5], "alphaVar": "moveForwardAlpha", "desiredSpeedVar": "moveForwardSpeed" }, @@ -568,28 +568,15 @@ }, { "id": "idleToWalkFwd", - "type": "blendLinearMove", + "type": "clip", "data": { - "alpha": 0.0, - "desiredSpeed": 1.2, - "characteristicSpeeds": [1.2], - "alphaVar": 0.0, - "desiredSpeedVar": "moveForwardSpeed" + "url": "animations/idle_to_walk.fbx", + "startFrame": 1.0, + "endFrame": 13.0, + "timeScale": 1.0, + "loopFlag": false }, - "children": [ - { - "id": "idleToWalkFwdClip", - "type": "clip", - "data": { - "url": "animations/idle_to_walk.fbx", - "startFrame": 1.0, - "endFrame": 13.0, - "timeScale": 0.9, - "loopFlag": false - }, - "children": [] - } - ] + "children": [] }, { "id": "walkBwd", @@ -597,7 +584,7 @@ "data": { "alpha": 0.0, "desiredSpeed": 1.4, - "characteristicSpeeds": [0.6, 1.05], + "characteristicSpeeds": [0.6, 1.45], "alphaVar": "moveBackwardAlpha", "desiredSpeedVar": "moveBackwardSpeed" }, @@ -659,7 +646,7 @@ "data": { "alpha": 0.0, "desiredSpeed": 1.4, - "characteristicSpeeds": [0.2, 0.5], + "characteristicSpeeds": [0.2, 0.65], "alphaVar": "moveLateralAlpha", "desiredSpeedVar": "moveLateralSpeed" }, @@ -696,7 +683,7 @@ "data": { "alpha": 0.0, "desiredSpeed": 1.4, - "characteristicSpeeds": [0.2, 0.5], + "characteristicSpeeds": [0.2, 0.65], "alphaVar": "moveLateralAlpha", "desiredSpeedVar": "moveLateralSpeed" }, diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 900d1bbb63..cff4d6a614 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3882,6 +3882,13 @@ void Application::update(float deltaTime) { if (nearbyEntitiesAreReadyForPhysics()) { _physicsEnabled = true; getMyAvatar()->updateMotionBehaviorFromMenu(); + } else { + auto characterController = getMyAvatar()->getCharacterController(); + if (characterController) { + // if we have a character controller, disable it here so the avatar doesn't get stuck due to + // a non-loading collision hull. + characterController->setEnabled(false); + } } } } @@ -4010,7 +4017,7 @@ void Application::update(float deltaTime) { avatarManager->getObjectsToChange(motionStates); _physicsEngine->changeObjects(motionStates); - myAvatar->prepareForPhysicsSimulation(deltaTime); + myAvatar->prepareForPhysicsSimulation(); _physicsEngine->forEachAction([&](EntityActionPointer action) { action->prepareForPhysicsSimulation(); }); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index ea1b26dd80..dfa1a2f8b9 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -498,10 +498,6 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderMyLookAtVectors, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderOtherLookAtVectors, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisplayLeftFootTrace, 0, false, - avatar.get(), SLOT(setEnableDebugDrawLeftFootTrace(bool))); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisplayRightFootTrace, 0, false, - avatar.get(), SLOT(setEnableDebugDrawRightFootTrace(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawDefaultPose, 0, false, avatar.get(), SLOT(setEnableDebugDrawDefaultPose(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawAnimPose, 0, false, @@ -518,8 +514,6 @@ Menu::Menu() { avatar.get(), SLOT(setEnableInverseKinematics(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderSensorToWorldMatrix, 0, false, avatar.get(), SLOT(setEnableDebugDrawSensorToWorldMatrix(bool))); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderIKTargets, 0, false, - avatar.get(), SLOT(setEnableDebugDrawIKTargets(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ActionMotorControl, Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar.get(), SLOT(updateMotionBehaviorFromMenu()), @@ -529,18 +523,10 @@ Menu::Menu() { avatar.get(), SLOT(updateMotionBehaviorFromMenu()), UNSPECIFIED_POSITION, "Developer"); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableAvatarCollisions, 0, true, + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableCharacterController, 0, true, avatar.get(), SLOT(updateMotionBehaviorFromMenu()), UNSPECIFIED_POSITION, "Developer"); - // KINEMATIC_CONTROLLER_HACK - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::MoveKinematically, 0, false, - avatar.get(), SLOT(updateMotionBehaviorFromMenu()), - UNSPECIFIED_POSITION, "Developer"); - - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableVerticalComfortMode, 0, false, - avatar.get(), SLOT(setEnableVerticalComfortMode(bool))); - // Developer > Hands >>> MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands"); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index ad11b91796..e339da4d25 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -87,8 +87,6 @@ namespace MenuOption { const QString DiskCacheEditor = "Disk Cache Editor"; const QString DisplayCrashOptions = "Display Crash Options"; const QString DisplayHandTargets = "Show Hand Targets"; - const QString DisplayLeftFootTrace = "Show Left Foot Trace"; - const QString DisplayRightFootTrace = "Show Right Foot Trace"; const QString DisplayModelBounds = "Display Model Bounds"; const QString DisplayModelTriangles = "Display Model Triangles"; const QString DisplayModelElementChildProxies = "Display Model Element Children"; @@ -98,11 +96,8 @@ namespace MenuOption { const QString DontRenderEntitiesAsScene = "Don't Render Entities as Scene"; const QString EchoLocalAudio = "Echo Local Audio"; const QString EchoServerAudio = "Echo Server Audio"; - const QString EnableAvatarCollisions = "Enable Avatar Collisions"; - const QString EnableIncrementalTextureTransfer = "Enable Incremental Texture Transfer"; - const QString EnableDynamicTextureManagement = "Enable Dynamic Texture Management"; + const QString EnableCharacterController = "Enable avatar collisions"; const QString EnableInverseKinematics = "Enable Inverse Kinematics"; - const QString EnableVerticalComfortMode = "Enable Vertical Comfort Mode"; const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation"; const QString ExpandMyAvatarTiming = "Expand /myAvatar"; const QString ExpandOtherAvatarTiming = "Expand /otherAvatar"; @@ -168,7 +163,6 @@ namespace MenuOption { const QString RenderResolutionThird = "1/3"; const QString RenderResolutionQuarter = "1/4"; const QString RenderSensorToWorldMatrix = "Show SensorToWorld Matrix"; - const QString RenderIKTargets = "Show IK Targets"; const QString ResetAvatarSize = "Reset Avatar Size"; const QString ResetSensors = "Reset Sensors"; const QString RunningScripts = "Running Scripts..."; @@ -197,7 +191,6 @@ namespace MenuOption { const QString UseAudioForMouth = "Use Audio for Mouth"; const QString UseCamera = "Use Camera"; const QString UseAnimPreAndPostRotations = "Use Anim Pre and Post Rotations"; - const QString MoveKinematically = "Move Kinematically"; // KINEMATIC_CONTROLLER_HACK const QString VelocityFilter = "Velocity Filter"; const QString VisibleToEveryone = "Everyone"; const QString VisibleToFriends = "Friends"; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp old mode 100755 new mode 100644 index 9a0b04aa6d..b8f02fd17e --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include @@ -61,7 +60,7 @@ using namespace std; const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f); const float DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES = 30.0f; -const float MAX_WALKING_SPEED = 2.0f; // human walking speed +const float MAX_WALKING_SPEED = 2.6f; // human walking speed const float MAX_BOOST_SPEED = 0.5f * MAX_WALKING_SPEED; // action motor gets additive boost below this speed const float MIN_AVATAR_SPEED = 0.05f; const float MIN_AVATAR_SPEED_SQUARED = MIN_AVATAR_SPEED * MIN_AVATAR_SPEED; // speed is set to zero below this @@ -84,13 +83,6 @@ const float MyAvatar::ZOOM_MIN = 0.5f; const float MyAvatar::ZOOM_MAX = 25.0f; const float MyAvatar::ZOOM_DEFAULT = 1.5f; -// OUTOFBODY_HACK defined in SkeletonModel.cpp -extern glm::vec3 TRUNCATE_IK_CAPSULE_POSITION; -extern float TRUNCATE_IK_CAPSULE_LENGTH; -extern float TRUNCATE_IK_CAPSULE_RADIUS; -extern float MIN_OUT_OF_BODY_DISTANCE; -extern float MAX_OUT_OF_BODY_DISTANCE; - MyAvatar::MyAvatar(RigPointer rig) : Avatar(rig), _wasPushing(false), @@ -147,6 +139,8 @@ MyAvatar::MyAvatar(RigPointer rig) : // when we leave a domain we lift whatever restrictions that domain may have placed on our scale connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &MyAvatar::clearScaleRestriction); + _characterController.setEnabled(true); + _bodySensorMatrix = deriveBodyFromHMDSensor(); using namespace recording; @@ -376,27 +370,6 @@ void MyAvatar::update(float deltaTime) { simulate(deltaTime); - // Request to show the hand controllers if we're out-of-body for more then HAND_CONTROLLER_SHOW_TIME. - // Similarlly request to hide the controllers when we return to our bodies. - const float HAND_CONTROLLER_SHOW_TIME = 0.75f; - auto hmdInterface = DependencyManager::get(); - if (isOutOfBody() != _handControllerShow) { - _handControllerShowTimer += deltaTime; - if (_handControllerShowTimer > HAND_CONTROLLER_SHOW_TIME) { - if (isOutOfBody()) { - hmdInterface->requestShowHandControllers(); - _handControllerShow = true; - _handControllerShowTimer = 0.0f; - } else { - hmdInterface->requestHideHandControllers(); - _handControllerShow = false; - _handControllerShowTimer = 0.0f; - } - } - } else { - _handControllerShowTimer = 0.0f; - } - currentEnergy += energyChargeRate; currentEnergy -= getAccelerationEnergy(); currentEnergy -= getAudioEnergy(); @@ -464,18 +437,10 @@ void MyAvatar::simulate(float deltaTime) { // update sensorToWorldMatrix for camera and hand controllers // before we perform rig animations and IK. - - if (_characterController.getState() != CharacterController::State::Hover) { - updateSensorToWorldMatrix(_enableVerticalComfortMode ? SensorToWorldUpdateMode::VerticalComfort : SensorToWorldUpdateMode::Vertical); - } + updateSensorToWorldMatrix(); { PerformanceTimer perfTimer("skeleton"); - - if (_rig) { - _rig->setEnableDebugDrawIKTargets(_enableDebugDrawIKTargets); - } - _skeletonModel->simulate(deltaTime); } @@ -556,8 +521,8 @@ void MyAvatar::simulate(float deltaTime) { } }); _characterController.setFlyingAllowed(flyingAllowed); - if (!ghostingAllowed && _characterController.getCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) { - _characterController.setCollisionGroup(BULLET_COLLISION_GROUP_MY_AVATAR); + if (!_characterController.isEnabled() && !ghostingAllowed) { + _characterController.setEnabled(true); } } @@ -600,27 +565,11 @@ void MyAvatar::updateJointFromController(controller::Action poseKey, ThreadSafeV // best called at end of main loop, after physics. // update sensor to world matrix from current body position and hmd sensor. // This is so the correct camera can be used for rendering. -void MyAvatar::updateSensorToWorldMatrix(SensorToWorldUpdateMode mode) { - if (mode == SensorToWorldUpdateMode::Full) { - glm::mat4 bodyToWorld = createMatFromQuatAndPos(getOrientation(), getPosition()); - setSensorToWorldMatrix(bodyToWorld * glm::inverse(_bodySensorMatrix)); - } else if (mode == SensorToWorldUpdateMode::Vertical || - mode == SensorToWorldUpdateMode::VerticalComfort) { - glm::mat4 bodyToWorld = createMatFromQuatAndPos(getOrientation(), getPosition()); - glm::mat4 newSensorToWorldMat = bodyToWorld * glm::inverse(_bodySensorMatrix); - glm::mat4 sensorToWorldMat = _sensorToWorldMatrix; - sensorToWorldMat[3][1] = newSensorToWorldMat[3][1]; - if (mode == SensorToWorldUpdateMode::VerticalComfort && - fabsf(_sensorToWorldMatrix[3][1] - newSensorToWorldMat[3][1]) > 0.1f) { - setSensorToWorldMatrix(sensorToWorldMat); - } else if (mode == SensorToWorldUpdateMode::Vertical) { - setSensorToWorldMatrix(sensorToWorldMat); - } - } -} - -void MyAvatar::setSensorToWorldMatrix(const glm::mat4& sensorToWorld) { - _sensorToWorldMatrix = sensorToWorld; +void MyAvatar::updateSensorToWorldMatrix() { + // update the sensor mat so that the body position will end up in the desired + // position when driven from the head. + glm::mat4 desiredMat = createMatFromQuatAndPos(getOrientation(), getPosition()); + _sensorToWorldMatrix = desiredMat * glm::inverse(_bodySensorMatrix); lateUpdatePalms(); @@ -862,14 +811,6 @@ float loadSetting(Settings& settings, const QString& name, float defaultValue) { return value; } -void MyAvatar::setEnableDebugDrawLeftFootTrace(bool isEnabled) { - _enableDebugDrawLeftFootTrace = isEnabled; -} - -void MyAvatar::setEnableDebugDrawRightFootTrace(bool isEnabled) { - _enableDebugDrawRightFootTrace = isEnabled; -} - void MyAvatar::setEnableDebugDrawDefaultPose(bool isEnabled) { _enableDebugDrawDefaultPose = isEnabled; @@ -912,11 +853,6 @@ void MyAvatar::setEnableDebugDrawSensorToWorldMatrix(bool isEnabled) { } } -void MyAvatar::setEnableDebugDrawIKTargets(bool isEnabled) { - _enableDebugDrawIKTargets = isEnabled; -} - - void MyAvatar::setEnableMeshVisible(bool isEnabled) { render::ScenePointer scene = qApp->getMain3DScene(); _skeletonModel->setVisibleInScene(isEnabled, scene); @@ -931,10 +867,6 @@ void MyAvatar::setEnableInverseKinematics(bool isEnabled) { _rig->setEnableInverseKinematics(isEnabled); } -void MyAvatar::setEnableVerticalComfortMode(bool isEnabled) { - _enableVerticalComfortMode = isEnabled; -} - void MyAvatar::loadData() { Settings settings; settings.beginGroup("Avatar"); @@ -1282,8 +1214,6 @@ void MyAvatar::rebuildCollisionShape() { float scale = getUniformScale(); float radius = scale * _skeletonModel->getBoundingCapsuleRadius(); float height = scale * _skeletonModel->getBoundingCapsuleHeight() + 2.0f * radius; - const float CANONICAL_AVATAR_HEIGHT = 2.0f; - _canonicalScale = height / CANONICAL_AVATAR_HEIGHT; glm::vec3 corner(-radius, -0.5f * height, -radius); corner += scale * _skeletonModel->getBoundingCapsuleOffset(); glm::vec3 diagonal(2.0f * radius, height, 2.0f * radius); @@ -1341,14 +1271,10 @@ controller::Pose MyAvatar::getRightHandControllerPoseInAvatarFrame() const { } void MyAvatar::updateMotors() { - const float DEFAULT_MOTOR_TIMESCALE = 0.2f; - const float INVALID_MOTOR_TIMESCALE = 1.0e6f; - _characterController.clearMotors(); glm::quat motorRotation; if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) { - if (_characterController.getState() == CharacterController::State::Hover || - _characterController.getCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) { + if (_characterController.getState() == CharacterController::State::Hover) { motorRotation = getHead()->getCameraOrientation(); } else { // non-hovering = walking: follow camera twist about vertical but not lift @@ -1356,18 +1282,14 @@ void MyAvatar::updateMotors() { glm::quat liftRotation; swingTwistDecomposition(getHead()->getCameraOrientation(), _worldUpDirection, liftRotation, motorRotation); } - - if (qApp->isHMDMode()) { - // OUTOFBODY_HACK: in HMDMode motors are applied differently: a "follow" motor is added - // during the CharacterController's substep + const float DEFAULT_MOTOR_TIMESCALE = 0.2f; + const float INVALID_MOTOR_TIMESCALE = 1.0e6f; + if (_isPushing || _isBraking || !_isBeingPushed) { + _characterController.addMotor(_actionMotorVelocity, motorRotation, DEFAULT_MOTOR_TIMESCALE, INVALID_MOTOR_TIMESCALE); } else { - if (_isPushing || _isBraking || !_isBeingPushed) { - _characterController.addMotor(_actionMotorVelocity, motorRotation, DEFAULT_MOTOR_TIMESCALE, INVALID_MOTOR_TIMESCALE); - } else { - // _isBeingPushed must be true --> disable action motor by giving it a long timescale, - // otherwise it's attempt to "stand in in place" could defeat scripted motor/thrusts - _characterController.addMotor(_actionMotorVelocity, motorRotation, INVALID_MOTOR_TIMESCALE); - } + // _isBeingPushed must be true --> disable action motor by giving it a long timescale, + // otherwise it's attempt to "stand in in place" could defeat scripted motor/thrusts + _characterController.addMotor(_actionMotorVelocity, motorRotation, INVALID_MOTOR_TIMESCALE); } } if (_motionBehaviors & AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED) { @@ -1379,19 +1301,16 @@ void MyAvatar::updateMotors() { // world-frame motorRotation = glm::quat(); } - if (qApp->isHMDMode()) { - // OUTOFBODY_HACK: motors are applied differently in HMDMode - } else { - _characterController.addMotor(_scriptedMotorVelocity, motorRotation, _scriptedMotorTimescale); - } + _characterController.addMotor(_scriptedMotorVelocity, motorRotation, _scriptedMotorTimescale); } // legacy support for 'MyAvatar::applyThrust()', which has always been implemented as a // short-lived linearAcceleration - _characterController.setLinearAcceleration(glm::vec3(0.0f, _thrust.y, 0.0f)); + _characterController.setLinearAcceleration(_thrust); + _thrust = Vectors::ZERO; } -void MyAvatar::prepareForPhysicsSimulation(float deltaTime) { +void MyAvatar::prepareForPhysicsSimulation() { relayDriveKeysToCharacterController(); updateMotors(); @@ -1401,54 +1320,32 @@ void MyAvatar::prepareForPhysicsSimulation(float deltaTime) { qDebug() << "Warning: getParentVelocity failed" << getID(); parentVelocity = glm::vec3(); } - _characterController.handleChangedCollisionGroup(); _characterController.setParentVelocity(parentVelocity); - glm::vec3 position = getPosition(); - glm::quat orientation = getOrientation(); - - _characterController.setPositionAndOrientation(position, orientation); + _characterController.setPositionAndOrientation(getPosition(), getOrientation()); if (qApp->isHMDMode()) { - // update the _bodySensorMatrix based on leaning behavior of the avatar. - _bodySensorMatrix = _follow.prePhysicsUpdate(*this, deriveBodyFromHMDSensor(), _bodySensorMatrix, hasDriveInput(), deltaTime); - - // The avatar physics body always follows the _bodySensorMatrix. - glm::mat4 worldBodyMatrix = _sensorToWorldMatrix * _bodySensorMatrix; - getCharacterController()->setFollowParameters(worldBodyMatrix); + _follow.prePhysicsUpdate(*this, deriveBodyFromHMDSensor(), _bodySensorMatrix, hasDriveInput()); } else { _follow.deactivate(); - getCharacterController()->disableFollow(); } _prePhysicsRoomPose = AnimPose(_sensorToWorldMatrix); } void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaTime) { - // figure out how far the hips can move before they hit something - int hipsJoint = getJointIndex("Hips"); - glm::vec3 hipsPosition; // rig-frame - // OUTOFBODY_HACK -- hardcoded maxHipsOffsetRadius (ultimately must exceed FollowHelper lateral/forward/back walk thresholds) - float maxHipsOffsetRadius = 3.0f * _characterController.getCapsuleRadius(); - if (_rig->getJointPosition(hipsJoint, hipsPosition)) { - // OUTOFBODY_HACK -- flip PI about yAxis - hipsPosition.x *= -1.0f; - hipsPosition.z *= -1.0f; - maxHipsOffsetRadius = _characterController.measureMaxHipsOffsetRadius(hipsPosition, maxHipsOffsetRadius); - } - _rig->updateMaxHipsOffsetLength(maxHipsOffsetRadius, deltaTime); - glm::vec3 position = getPosition(); glm::quat orientation = getOrientation(); if (_characterController.isEnabledAndReady()) { _characterController.getPositionAndOrientation(position, orientation); } nextAttitude(position, orientation); + _bodySensorMatrix = _follow.postPhysicsUpdate(*this, _bodySensorMatrix); if (_characterController.isEnabledAndReady()) { - setVelocity(_characterController.getLinearVelocity()); + setVelocity(_characterController.getLinearVelocity() + _characterController.getFollowVelocity()); + } else { + setVelocity(getVelocity() + _characterController.getFollowVelocity()); } - - _follow.postPhysicsUpdate(*this); } QString MyAvatar::getScriptedMotorFrame() const { @@ -1674,26 +1571,8 @@ void MyAvatar::postUpdate(float deltaTime) { DebugDraw::getInstance().updateMyAvatarPos(getPosition()); DebugDraw::getInstance().updateMyAvatarRot(getOrientation()); - if (_enableDebugDrawLeftFootTrace || _enableDebugDrawRightFootTrace) { - int boneIndex = _enableDebugDrawLeftFootTrace ? getJointIndex("LeftFoot") : getJointIndex("RightFoot"); - const glm::vec4 RED(1.0f, 0.0f, 0.0f, 1.0f); - const glm::vec4 WHITE(1.0f, 1.0f, 1.0f, 1.0f); - const glm::vec4 TRANS(1.0f, 1.0f, 1.0f, 0.0f); - static bool colorBit = true; - colorBit = !colorBit; - glm::vec4 color = colorBit ? RED : WHITE; - - _debugLineLoop[_debugLineLoopIndex] = DebugDrawVertex(getJointPosition(boneIndex), color); - _debugLineLoopIndex = (_debugLineLoopIndex + 1) % DEBUG_LINE_LOOP_SIZE; - _debugLineLoop[_debugLineLoopIndex] = DebugDrawVertex(getJointPosition(boneIndex), TRANS); - for (size_t prev = DEBUG_LINE_LOOP_SIZE - 1, next = 0; next < DEBUG_LINE_LOOP_SIZE; prev = next, next++) { - if (_debugLineLoop[prev].color.w > 0.0f) { - DebugDraw::getInstance().drawRay(_debugLineLoop[prev].pos, _debugLineLoop[next].pos, _debugLineLoop[prev].color); - } - } - } - AnimPose postUpdateRoomPose(_sensorToWorldMatrix); + updateHoldActions(_prePhysicsRoomPose, postUpdateRoomPose); } @@ -1789,20 +1668,11 @@ void MyAvatar::updateOrientation(float deltaTime) { // update body orientation by movement inputs - glm::quat deltaRotation = glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f))); - setOrientation(getOrientation() * deltaRotation); + setOrientation(getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f)))); getHead()->setBasePitch(getHead()->getBasePitch() + _driveKeys[PITCH] * _pitchSpeed * deltaTime); if (qApp->isHMDMode()) { - - // rotate the sensorToWorldMatrix about the HMD! - glm::vec3 hmdOffset = extractTranslation(getHMDSensorMatrix()); - _sensorToWorldMatrix = (_sensorToWorldMatrix * - createMatFromQuatAndPos(glm::quat(), hmdOffset) * - createMatFromQuatAndPos(deltaRotation, glm::vec3()) * - createMatFromQuatAndPos(glm::quat(), -hmdOffset)); - glm::quat orientation = glm::quat_cast(getSensorToWorldMatrix()) * getHMDSensorOrientation(); glm::quat bodyOrientation = getWorldBodyOrientation(); glm::quat localOrientation = glm::inverse(bodyOrientation) * orientation; @@ -1819,7 +1689,6 @@ void MyAvatar::updateOrientation(float deltaTime) { } void MyAvatar::updateActionMotor(float deltaTime) { - bool thrustIsPushing = (glm::length2(_thrust) > EPSILON); bool scriptedMotorIsPushing = (_motionBehaviors & AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED) && _scriptedMotorTimescale < MAX_CHARACTER_MOTOR_TIMESCALE; @@ -1859,11 +1728,11 @@ void MyAvatar::updateActionMotor(float deltaTime) { if (state == CharacterController::State::Hover) { // we're flying --> complex acceleration curve that builds on top of current motor speed and caps at some max speed - float motorSpeed = glm::length(glm::vec3(_actionMotorVelocity.x, _actionMotorVelocity.y, _actionMotorVelocity.z)); + float motorSpeed = glm::length(_actionMotorVelocity); float finalMaxMotorSpeed = getUniformScale() * MAX_ACTION_MOTOR_SPEED; float speedGrowthTimescale = 2.0f; float speedIncreaseFactor = 1.8f; - motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale, 0.0f, 1.0f) * speedIncreaseFactor; + motorSpeed *= 1.0f + glm::clamp(deltaTime / speedGrowthTimescale , 0.0f, 1.0f) * speedIncreaseFactor; const float maxBoostSpeed = getUniformScale() * MAX_BOOST_SPEED; if (_isPushing) { @@ -1886,21 +1755,6 @@ void MyAvatar::updateActionMotor(float deltaTime) { _boomLength = glm::clamp(_boomLength, ZOOM_MIN, ZOOM_MAX); } -void MyAvatar::applyVelocityToSensorToWorldMatrix(const glm::vec3& velocity, float deltaTime) { - glm::vec3 newVelocity = velocity; - if (_characterController.getState() != CharacterController::State::Hover) { - newVelocity -= glm::dot(newVelocity, _worldUpDirection); - } - float speed2 = glm::length2(newVelocity); - if (speed2 > MIN_AVATAR_SPEED_SQUARED) { - glm::vec3 position = extractTranslation(_sensorToWorldMatrix) + deltaTime * newVelocity; - // update the position column of matrix - glm::mat4 newSensorToWorldMatrix = _sensorToWorldMatrix; - newSensorToWorldMatrix[3] = glm::vec4(position, 1.0f); - setSensorToWorldMatrix(newSensorToWorldMatrix); - } -} - void MyAvatar::updatePosition(float deltaTime) { if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) { updateActionMotor(deltaTime); @@ -1921,61 +1775,11 @@ void MyAvatar::updatePosition(float deltaTime) { measureMotionDerivatives(deltaTime); _moving = speed2 > MOVING_SPEED_THRESHOLD_SQUARED; } else { + // physics physics simulation updated elsewhere float speed2 = glm::length2(velocity); _moving = speed2 > MOVING_SPEED_THRESHOLD_SQUARED; - - if (_moving) { - // scan for walkability - glm::vec3 position = getPosition(); - MyCharacterController::RayShotgunResult result; - glm::vec3 step = deltaTime * (getRotation() * _actionMotorVelocity); - _characterController.testRayShotgun(position, step, result); - _characterController.setStepUpEnabled(result.walkable); - } - - if (qApp->isHMDMode()) { - glm::quat motorRotation; - glm::vec3 worldVelocity = glm::vec3(0.0f); - if (_motionBehaviors & AVATAR_MOTION_ACTION_MOTOR_ENABLED) { - if (_characterController.getState() == CharacterController::State::Hover || - _characterController.getCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS) { - motorRotation = glmExtractRotation(_sensorToWorldMatrix * getHMDSensorMatrix()); - } else { - glm::quat liftRotation; - swingTwistDecomposition(glmExtractRotation(_sensorToWorldMatrix * getHMDSensorMatrix()), _worldUpDirection, liftRotation, motorRotation); - } - worldVelocity = motorRotation * _actionMotorVelocity; - } - - if (_motionBehaviors & AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED) { - if (_scriptedMotorFrame == SCRIPTED_MOTOR_CAMERA_FRAME) { - motorRotation = getHead()->getCameraOrientation() * glm::angleAxis(PI, Vectors::UNIT_Y); - } else if (_scriptedMotorFrame == SCRIPTED_MOTOR_AVATAR_FRAME) { - motorRotation = getOrientation() * glm::angleAxis(PI, Vectors::UNIT_Y); - } else { - // world-frame - motorRotation = glm::quat(); - } - worldVelocity += motorRotation * _scriptedMotorVelocity; - } - - // OUTOFBODY_HACK: apply scaling factor to _thrust, to get the same behavior as an periodically applied motor. - const float THRUST_DAMPING_FACTOR = 0.25f; - worldVelocity += THRUST_DAMPING_FACTOR * _thrust; - - // apply velocity directly to _sensorToWorldMatrix. - if (glm::length2(worldVelocity) > FLT_EPSILON) { - glm::mat4 worldBodyMatrix = _sensorToWorldMatrix * _bodySensorMatrix; - glm::vec3 position = extractTranslation(worldBodyMatrix); - glm::vec3 step = deltaTime * worldVelocity; - glm::vec3 newVelocity = _characterController.computeHMDStep(position, step) / deltaTime; - applyVelocityToSensorToWorldMatrix(newVelocity, deltaTime); - } - } } - _thrust = Vectors::ZERO; - // 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)) { _hoverReferenceCameraFacingIsCaptured = true; @@ -2028,10 +1832,6 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float return false; } -glm::vec3 MyAvatar::getPreActionVelocity() const { - return _characterController.getPreActionLinearVelocity(); -} - // There can be a separation between the _targetScale and the actual scale of the rendered avatar in a domain. // When the avatar enters a domain where their target scale is not allowed according to the min/max // we do not change their saved target scale. Instead, we use getDomainLimitedScale() to render the avatar @@ -2215,17 +2015,13 @@ void MyAvatar::updateMotionBehaviorFromMenu() { _motionBehaviors &= ~AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED; } - // KINEMATIC_CONTROLLER_HACK - bool moveKinematically = menu->isOptionChecked(MenuOption::MoveKinematically); - _characterController.setMoveKinematically(moveKinematically); - - setAvatarCollisionsEnabled(menu->isOptionChecked(MenuOption::EnableAvatarCollisions)); + setCharacterControllerEnabled(menu->isOptionChecked(MenuOption::EnableCharacterController)); } -void MyAvatar::setAvatarCollisionsEnabled(bool enabled) { +void MyAvatar::setCharacterControllerEnabled(bool enabled) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setAvatarCollisionsEnabled", Q_ARG(bool, enabled)); + QMetaObject::invokeMethod(this, "setCharacterControllerEnabled", Q_ARG(bool, enabled)); return; } @@ -2237,12 +2033,11 @@ void MyAvatar::setAvatarCollisionsEnabled(bool enabled) { ghostingAllowed = zone->getGhostingAllowed(); } } - int16_t group = enabled || !ghostingAllowed ? BULLET_COLLISION_GROUP_MY_AVATAR : BULLET_COLLISION_GROUP_COLLISIONLESS; - _characterController.setCollisionGroup(group); + _characterController.setEnabled(ghostingAllowed ? enabled : true); } -bool MyAvatar::getAvatarCollisionsEnabled() { - return _characterController.getCollisionGroup() != BULLET_COLLISION_GROUP_COLLISIONLESS; +bool MyAvatar::getCharacterControllerEnabled() { + return _characterController.isEnabled(); } void MyAvatar::clearDriveKeys() { @@ -2330,10 +2125,6 @@ glm::quat MyAvatar::getOrientationForAudio() { return quat(); } -bool MyAvatar::isOutOfBody() const { - return _follow._isOutOfBody; -} - void MyAvatar::setAudioListenerMode(AudioListenerMode audioListenerMode) { if (_audioListenerMode != audioListenerMode) { _audioListenerMode = audioListenerMode; @@ -2354,52 +2145,72 @@ void MyAvatar::lateUpdatePalms() { Avatar::updatePalms(); } + +static const float FOLLOW_TIME = 0.5f; + MyAvatar::FollowHelper::FollowHelper() { deactivate(); } void MyAvatar::FollowHelper::deactivate() { - _activeBits = 0; + for (int i = 0; i < NumFollowTypes; i++) { + deactivate((FollowType)i); + } } void MyAvatar::FollowHelper::deactivate(FollowType type) { assert(type >= 0 && type < NumFollowTypes); - _activeBits &= ~(uint8_t)(0x01 << (int)type); + _timeRemaining[(int)type] = 0.0f; } void MyAvatar::FollowHelper::activate(FollowType type) { assert(type >= 0 && type < NumFollowTypes); - _activeBits |= (uint8_t)(0x01 << (int)type); + // TODO: Perhaps, the follow time should be proportional to the displacement. + _timeRemaining[(int)type] = FOLLOW_TIME; } bool MyAvatar::FollowHelper::isActive(FollowType type) const { assert(type >= 0 && type < NumFollowTypes); - return (bool)(_activeBits & (uint8_t)(0x01 << (int)type)); + return _timeRemaining[(int)type] > 0.0f; } bool MyAvatar::FollowHelper::isActive() const { - return (bool)_activeBits; + for (int i = 0; i < NumFollowTypes; i++) { + if (isActive((FollowType)i)) { + return true; + } + } + return false; } -void MyAvatar::FollowHelper::updateRotationActivation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) { - auto cameraMode = qApp->getCamera()->getMode(); - if (cameraMode == CAMERA_MODE_THIRD_PERSON) { - deactivate(Rotation); - } else { - const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees - const float STOP_FOLLOW_ROTATION_THRESHOLD = cosf(PI / 180.0f); // 1 degree - glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix); - if (isActive(Rotation)) { - if (glm::dot(myAvatar.getHMDSensorFacing(), bodyFacing) > STOP_FOLLOW_ROTATION_THRESHOLD) { - deactivate(Rotation); - } - } else if (glm::dot(myAvatar.getHMDSensorFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD) { - activate(Rotation); +float MyAvatar::FollowHelper::getMaxTimeRemaining() const { + float max = 0.0f; + for (int i = 0; i < NumFollowTypes; i++) { + if (_timeRemaining[i] > max) { + max = _timeRemaining[i]; } } + return max; +} + +void MyAvatar::FollowHelper::decrementTimeRemaining(float dt) { + for (int i = 0; i < NumFollowTypes; i++) { + _timeRemaining[i] -= dt; + } } -void MyAvatar::FollowHelper::updateHorizontalActivation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) { +bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const { + auto cameraMode = qApp->getCamera()->getMode(); + if (cameraMode == CAMERA_MODE_THIRD_PERSON) { + return false; + } else { + const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees + glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix); + return glm::dot(myAvatar.getHMDSensorFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD; + } +} + +bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const { // -z axis of currentBodyMatrix in world space. glm::vec3 forward = glm::normalize(glm::vec3(-currentBodyMatrix[0][2], -currentBodyMatrix[1][2], -currentBodyMatrix[2][2])); @@ -2408,86 +2219,88 @@ void MyAvatar::FollowHelper::updateHorizontalActivation(const MyAvatar& myAvatar glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix); float forwardLeanAmount = glm::dot(forward, offset); - float lateralLeanAmount = fabsf(glm::dot(right, offset)); + float lateralLeanAmount = glm::dot(right, offset); const float MAX_LATERAL_LEAN = 0.3f; const float MAX_FORWARD_LEAN = 0.15f; const float MAX_BACKWARD_LEAN = 0.1f; - const float MIN_LEAN = 0.02f; - if (isActive(Horizontal)) { - if (fabsf(forwardLeanAmount) < MIN_LEAN && lateralLeanAmount < MIN_LEAN) { - deactivate(Horizontal); - } - } else { - if (forwardLeanAmount > MAX_FORWARD_LEAN || - forwardLeanAmount < -MAX_BACKWARD_LEAN || - lateralLeanAmount > MAX_LATERAL_LEAN) { - activate(Horizontal); - } + if (forwardLeanAmount > 0 && forwardLeanAmount > MAX_FORWARD_LEAN) { + return true; + } else if (forwardLeanAmount < 0 && forwardLeanAmount < -MAX_BACKWARD_LEAN) { + return true; } + + return fabs(lateralLeanAmount) > MAX_LATERAL_LEAN; } -void MyAvatar::FollowHelper::updateVerticalActivation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) { +bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const { + const float CYLINDER_TOP = 0.1f; const float CYLINDER_BOTTOM = -1.5f; - const float MIN_VERTICAL_OFFSET = 0.02f; glm::vec3 offset = extractTranslation(desiredBodyMatrix) - extractTranslation(currentBodyMatrix); - if (isActive(Vertical)) { - if (fabsf(offset.y) < MIN_VERTICAL_OFFSET) { - deactivate(Vertical); - } - } else if (offset.y > CYLINDER_TOP || offset.y < CYLINDER_BOTTOM) { - activate(Vertical); - } + return (offset.y > CYLINDER_TOP) || (offset.y < CYLINDER_BOTTOM); } -glm::mat4 MyAvatar::FollowHelper::prePhysicsUpdate(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput, float deltaTime) { +void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput) { + _desiredBodyMatrix = desiredBodyMatrix; + if (myAvatar.getHMDLeanRecenterEnabled()) { - updateRotationActivation(myAvatar, desiredBodyMatrix, currentBodyMatrix); - updateHorizontalActivation(myAvatar, desiredBodyMatrix, currentBodyMatrix); - updateVerticalActivation(myAvatar, desiredBodyMatrix, currentBodyMatrix); + if (!isActive(Rotation) && shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix)) { + activate(Rotation); + } + if (!isActive(Horizontal) && shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix)) { + activate(Horizontal); + } + if (!isActive(Vertical) && (shouldActivateVertical(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { + activate(Vertical); + } + } - AnimPose currentBodyPose(currentBodyMatrix); - AnimPose desiredBodyPose(desiredBodyMatrix); - AnimPose followBodyPose(currentBodyMatrix); + glm::mat4 desiredWorldMatrix = myAvatar.getSensorToWorldMatrix() * _desiredBodyMatrix; + glm::mat4 currentWorldMatrix = myAvatar.getSensorToWorldMatrix() * currentBodyMatrix; - if (isActive(Rotation) || hasDriveInput) { - followBodyPose.rot = desiredBodyPose.rot; - } - if (isActive(Horizontal) || hasDriveInput) { - followBodyPose.trans.x = desiredBodyPose.trans.x; - followBodyPose.trans.z = desiredBodyPose.trans.z; - } - if (isActive(Vertical) || hasDriveInput) { - followBodyPose.trans.y = desiredBodyPose.trans.y; - } + AnimPose followWorldPose(currentWorldMatrix); + if (isActive(Rotation)) { + followWorldPose.rot = glmExtractRotation(desiredWorldMatrix); + } + if (isActive(Horizontal)) { + glm::vec3 desiredTranslation = extractTranslation(desiredWorldMatrix); + followWorldPose.trans.x = desiredTranslation.x; + followWorldPose.trans.z = desiredTranslation.z; + } + if (isActive(Vertical)) { + glm::vec3 desiredTranslation = extractTranslation(desiredWorldMatrix); + followWorldPose.trans.y = desiredTranslation.y; + } + + myAvatar.getCharacterController()->setFollowParameters(followWorldPose, getMaxTimeRemaining()); +} + +glm::mat4 MyAvatar::FollowHelper::postPhysicsUpdate(const MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix) { + if (isActive()) { + float dt = myAvatar.getCharacterController()->getFollowTime(); + decrementTimeRemaining(dt); + + // apply follow displacement to the body matrix. + glm::vec3 worldLinearDisplacement = myAvatar.getCharacterController()->getFollowLinearDisplacement(); + glm::quat worldAngularDisplacement = myAvatar.getCharacterController()->getFollowAngularDisplacement(); + glm::quat sensorToWorld = glmExtractRotation(myAvatar.getSensorToWorldMatrix()); + glm::quat worldToSensor = glm::inverse(sensorToWorld); + + glm::vec3 sensorLinearDisplacement = worldToSensor * worldLinearDisplacement; + glm::quat sensorAngularDisplacement = worldToSensor * worldAngularDisplacement * sensorToWorld; + + glm::mat4 newBodyMat = createMatFromQuatAndPos(sensorAngularDisplacement * glmExtractRotation(currentBodyMatrix), + sensorLinearDisplacement + extractTranslation(currentBodyMatrix)); + return newBodyMat; - if (isActive() || hasDriveInput) { - const float TIMESCALE = 0.2f; - const float tau = glm::clamp(deltaTime / TIMESCALE, 0.0f, 1.0f); - AnimPose newBodyPose; - blend(1, ¤tBodyPose, &followBodyPose, tau, &newBodyPose); - return (glm::mat4)newBodyPose; - } else { - return currentBodyMatrix; - } } else { - deactivate(); return currentBodyMatrix; } } -void MyAvatar::FollowHelper::postPhysicsUpdate(MyAvatar& myAvatar) { - glm::mat4 worldHMDMat = myAvatar.getSensorToWorldMatrix() * myAvatar.getHMDSensorMatrix(); - glm::vec3 worldHMDPosition = extractTranslation(worldHMDMat); - glm::vec3 capsuleStart = myAvatar.getPosition() + Vectors::UNIT_Y * (TRUNCATE_IK_CAPSULE_LENGTH / 2.0f); - glm::vec3 capsuleEnd = myAvatar.getPosition() - Vectors::UNIT_Y * (TRUNCATE_IK_CAPSULE_LENGTH / 2.0f); - _isOutOfBody = !pointIsInsideCapsule(worldHMDPosition, capsuleStart, capsuleEnd, TRUNCATE_IK_CAPSULE_RADIUS); - _outOfBodyDistance = distanceFromCapsule(worldHMDPosition, capsuleStart, capsuleEnd, TRUNCATE_IK_CAPSULE_RADIUS); -} - float MyAvatar::getAccelerationEnergy() { glm::vec3 velocity = getVelocity(); int changeInVelocity = abs(velocity.length() - priorVelocity.length()); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index a5dc01b12b..f081ec533b 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -56,8 +56,6 @@ enum AudioListenerMode { }; Q_DECLARE_METATYPE(AudioListenerMode); -const size_t DEBUG_LINE_LOOP_SIZE = 500; - class MyAvatar : public Avatar { Q_OBJECT Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally) @@ -86,7 +84,7 @@ class MyAvatar : public Avatar { Q_PROPERTY(float energy READ getEnergy WRITE setEnergy) Q_PROPERTY(bool hmdLeanRecenterEnabled READ getHMDLeanRecenterEnabled WRITE setHMDLeanRecenterEnabled) - Q_PROPERTY(bool avatarCollisionsEnabled READ getAvatarCollisionsEnabled WRITE setAvatarCollisionsEnabled) + Q_PROPERTY(bool characterControllerEnabled READ getCharacterControllerEnabled WRITE setCharacterControllerEnabled) public: explicit MyAvatar(RigPointer rig); @@ -110,7 +108,6 @@ public: const glm::mat4& getHMDSensorMatrix() const { return _hmdSensorMatrix; } const glm::vec3& getHMDSensorPosition() const { return _hmdSensorPosition; } const glm::quat& getHMDSensorOrientation() const { return _hmdSensorOrientation; } - const glm::vec2& getHMDSensorFacing() const { return _hmdSensorFacing; } const glm::vec2& getHMDSensorFacingMovingAverage() const { return _hmdSensorFacingMovingAverage; } Q_INVOKABLE void setOrientationVar(const QVariant& newOrientationVar); @@ -128,14 +125,7 @@ public: // best called at end of main loop, just before rendering. // update sensor to world matrix from current body position and hmd sensor. // This is so the correct camera can be used for rendering. - enum class SensorToWorldUpdateMode { - Full = 0, - Vertical, - VerticalComfort - }; - void updateSensorToWorldMatrix(SensorToWorldUpdateMode mode = SensorToWorldUpdateMode::Full); - - void setSensorToWorldMatrix(const glm::mat4& sensorToWorld); + void updateSensorToWorldMatrix(); void setRealWorldFieldOfView(float realWorldFov) { _realWorldFieldOfView.set(realWorldFov); } @@ -237,7 +227,7 @@ public: const MyCharacterController* getCharacterController() const { return &_characterController; } void updateMotors(); - void prepareForPhysicsSimulation(float deltaTime); + void prepareForPhysicsSimulation(); void harvestResultsFromPhysicsSimulation(float deltaTime); const QString& getCollisionSoundURL() { return _collisionSoundURL; } @@ -282,14 +272,12 @@ public: bool hasDriveInput() const; - Q_INVOKABLE void setAvatarCollisionsEnabled(bool enabled); - Q_INVOKABLE bool getAvatarCollisionsEnabled(); + Q_INVOKABLE void setCharacterControllerEnabled(bool enabled); + Q_INVOKABLE bool getCharacterControllerEnabled(); virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override; virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override; - glm::vec3 getPreActionVelocity() const; - void addHoldAction(AvatarActionHold* holdAction); // thread-safe void removeHoldAction(AvatarActionHold* holdAction); // thread-safe void updateHoldActions(const AnimPose& prePhysicsPose, const AnimPose& postUpdatePose); @@ -314,19 +302,15 @@ public slots: Q_INVOKABLE void updateMotionBehaviorFromMenu(); - void setEnableDebugDrawLeftFootTrace(bool isEnabled); - void setEnableDebugDrawRightFootTrace(bool isEnabled); void setEnableDebugDrawDefaultPose(bool isEnabled); void setEnableDebugDrawAnimPose(bool isEnabled); void setEnableDebugDrawPosition(bool isEnabled); void setEnableDebugDrawHandControllers(bool isEnabled); void setEnableDebugDrawSensorToWorldMatrix(bool isEnabled); - void setEnableDebugDrawIKTargets(bool isEnabled); bool getEnableMeshVisible() const { return _skeletonModel->isVisible(); } void setEnableMeshVisible(bool isEnabled); void setUseAnimPreAndPostRotations(bool isEnabled); void setEnableInverseKinematics(bool isEnabled); - void setEnableVerticalComfortMode(bool isEnabled); QUrl getAnimGraphOverrideUrl() const; // thread-safe void setAnimGraphOverrideUrl(QUrl value); // thread-safe @@ -336,8 +320,6 @@ public slots: glm::vec3 getPositionForAudio(); glm::quat getOrientationForAudio(); - bool isOutOfBody() const; - signals: void audioListenerModeChanged(); void transformChanged(); @@ -390,8 +372,6 @@ private: virtual void updatePalms() override {} void lateUpdatePalms(); - void applyVelocityToSensorToWorldMatrix(const glm::vec3& velocity, float deltaTime); - void clampTargetScaleToDomainLimits(); void clampScaleChangeToDomainLimits(float desiredScale); glm::mat4 computeCameraRelativeHandControllerMatrix(const glm::mat4& controllerSensorMatrix) const; @@ -472,9 +452,8 @@ private: Vertical, NumFollowTypes }; - uint8_t _activeBits { 0 }; - bool _isOutOfBody { false }; - float _outOfBodyDistance { 0.0f }; + glm::mat4 _desiredBodyMatrix; + float _timeRemaining[NumFollowTypes]; void deactivate(); void deactivate(FollowType type); @@ -482,11 +461,13 @@ private: void activate(FollowType type); bool isActive() const; bool isActive(FollowType followType) const; - void updateRotationActivation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix); - void updateHorizontalActivation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix); - void updateVerticalActivation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix); - glm::mat4 prePhysicsUpdate(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput, float deltaTime); - void postPhysicsUpdate(MyAvatar& myAvatar); + float getMaxTimeRemaining() const; + void decrementTimeRemaining(float dt); + bool shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const; + bool shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const; + bool shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const; + void prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& bodySensorMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput); + glm::mat4 postPhysicsUpdate(const MyAvatar& myAvatar, const glm::mat4& currentBodyMatrix); }; FollowHelper _follow; @@ -498,14 +479,10 @@ private: RigPointer _rig; bool _prevShouldDrawHead; - bool _enableDebugDrawLeftFootTrace { false }; - bool _enableDebugDrawRightFootTrace { false }; bool _enableDebugDrawDefaultPose { false }; bool _enableDebugDrawAnimPose { false }; bool _enableDebugDrawHandControllers { false }; bool _enableDebugDrawSensorToWorldMatrix { false }; - bool _enableDebugDrawIKTargets { false }; - bool _enableVerticalComfortMode { false }; AudioListenerMode _audioListenerMode; glm::vec3 _customListenPosition; @@ -521,8 +498,6 @@ private: ThreadSafeValueCache _rightHandControllerPoseInSensorFrameCache { controller::Pose() }; bool _hmdLeanRecenterEnabled = true; - bool _moveKinematically { false }; // KINEMATIC_CONTROLLER_HACK - float _canonicalScale { 1.0f }; AnimPose _prePhysicsRoomPose; std::mutex _holdActionsMutex; @@ -540,18 +515,6 @@ private: float getEnergy(); void setEnergy(float value); bool didTeleport(); - - struct DebugDrawVertex { - DebugDrawVertex() : pos(), color() {} - DebugDrawVertex(const glm::vec3& posIn, const glm::vec4& colorIn) : pos(posIn), color(colorIn) {} - glm::vec3 pos; - glm::vec4 color; - }; - DebugDrawVertex _debugLineLoop[DEBUG_LINE_LOOP_SIZE]; - size_t _debugLineLoopIndex { 0 }; - - bool _handControllerShow { false }; - float _handControllerShowTimer { 0.0f }; }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); diff --git a/interface/src/avatar/MyCharacterController.cpp b/interface/src/avatar/MyCharacterController.cpp old mode 100755 new mode 100644 index 4161352e01..6e52f4a949 --- a/interface/src/avatar/MyCharacterController.cpp +++ b/interface/src/avatar/MyCharacterController.cpp @@ -15,15 +15,11 @@ #include "MyAvatar.h" +// TODO: improve walking up steps +// TODO: make avatars able to walk up and down steps/slopes // TODO: make avatars stand on steep slope // TODO: make avatars not snag on low ceilings - -void MyCharacterController::RayShotgunResult::reset() { - hitFraction = 1.0f; - walkable = true; -} - MyCharacterController::MyCharacterController(MyAvatar* avatar) { assert(avatar); @@ -34,33 +30,37 @@ MyCharacterController::MyCharacterController(MyAvatar* avatar) { MyCharacterController::~MyCharacterController() { } -void MyCharacterController::setDynamicsWorld(btDynamicsWorld* world) { - CharacterController::setDynamicsWorld(world); - if (world) { - initRayShotgun(world); - } -} - void MyCharacterController::updateShapeIfNecessary() { if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) { _pendingFlags &= ~PENDING_FLAG_UPDATE_SHAPE; + // compute new dimensions from avatar's bounding box + float x = _boxScale.x; + float z = _boxScale.z; + _radius = 0.5f * sqrtf(0.5f * (x * x + z * z)); + _halfHeight = 0.5f * _boxScale.y - _radius; + float MIN_HALF_HEIGHT = 0.1f; + if (_halfHeight < MIN_HALF_HEIGHT) { + _halfHeight = MIN_HALF_HEIGHT; + } + // NOTE: _shapeLocalOffset is already computed + if (_radius > 0.0f) { // create RigidBody if it doesn't exist if (!_rigidBody) { - btCollisionShape* shape = computeShape(); // HACK: use some simple mass property defaults for now - const btScalar DEFAULT_AVATAR_MASS = 100.0f; + const float DEFAULT_AVATAR_MASS = 100.0f; const btVector3 DEFAULT_AVATAR_INERTIA_TENSOR(30.0f, 8.0f, 30.0f); + btCollisionShape* shape = new btCapsuleShape(_radius, 2.0f * _halfHeight); _rigidBody = new btRigidBody(DEFAULT_AVATAR_MASS, nullptr, shape, DEFAULT_AVATAR_INERTIA_TENSOR); } else { btCollisionShape* shape = _rigidBody->getCollisionShape(); if (shape) { delete shape; } - shape = computeShape(); + shape = new btCapsuleShape(_radius, 2.0f * _halfHeight); _rigidBody->setCollisionShape(shape); } @@ -72,419 +72,12 @@ void MyCharacterController::updateShapeIfNecessary() { if (_state == State::Hover) { _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); } else { - _rigidBody->setGravity(_gravity * _currentUp); - } - // KINEMATIC_CONTROLLER_HACK - if (_moveKinematically) { - _rigidBody->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT); - } else { - _rigidBody->setCollisionFlags(_rigidBody->getCollisionFlags() & - ~(btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_STATIC_OBJECT)); + _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); } + //_rigidBody->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT); } else { // TODO: handle this failure case } } } -bool MyCharacterController::testRayShotgun(const glm::vec3& position, const glm::vec3& step, RayShotgunResult& result) { - btVector3 rayDirection = glmToBullet(step); - btScalar stepLength = rayDirection.length(); - if (stepLength < FLT_EPSILON) { - return false; - } - rayDirection /= stepLength; - - // get _ghost ready for ray traces - btTransform transform = _rigidBody->getWorldTransform(); - btVector3 newPosition = glmToBullet(position); - transform.setOrigin(newPosition); - _ghost.setWorldTransform(transform); - btMatrix3x3 rotation = transform.getBasis(); - _ghost.refreshOverlappingPairCache(); - - CharacterRayResult rayResult(&_ghost); - CharacterRayResult closestRayResult(&_ghost); - btVector3 rayStart; - btVector3 rayEnd; - - // compute rotation that will orient local ray start points to face step direction - btVector3 forward = rotation * btVector3(0.0f, 0.0f, -1.0f); - btVector3 adjustedDirection = rayDirection - rayDirection.dot(_currentUp) * _currentUp; - btVector3 axis = forward.cross(adjustedDirection); - btScalar lengthAxis = axis.length(); - if (lengthAxis > FLT_EPSILON) { - // we're walking sideways - btScalar angle = acosf(lengthAxis / adjustedDirection.length()); - if (rayDirection.dot(forward) < 0.0f) { - angle = PI - angle; - } - axis /= lengthAxis; - rotation = btMatrix3x3(btQuaternion(axis, angle)) * rotation; - } else if (rayDirection.dot(forward) < 0.0f) { - // we're walking backwards - rotation = btMatrix3x3(btQuaternion(_currentUp, PI)) * rotation; - } - - // scan the top - // NOTE: if we scan an extra distance forward we can detect flat surfaces that are too steep to walk on. - // The approximate extra distance can be derived with trigonometry. - // - // minimumForward = [ (maxStepHeight + radius / cosTheta - radius) * (cosTheta / sinTheta) - radius ] - // - // where: theta = max angle between floor normal and vertical - // - // if stepLength is not long enough we can add the difference. - // - btScalar cosTheta = _minFloorNormalDotUp; - btScalar sinTheta = sqrtf(1.0f - cosTheta * cosTheta); - const btScalar MIN_FORWARD_SLOP = 0.12f; // HACK: not sure why this is necessary to detect steepest walkable slope - btScalar forwardSlop = (_maxStepHeight + _radius / cosTheta - _radius) * (cosTheta / sinTheta) - (_radius + stepLength) + MIN_FORWARD_SLOP; - if (forwardSlop < 0.0f) { - // BIG step, no slop necessary - forwardSlop = 0.0f; - } - - const btScalar backSlop = 0.04f; - for (int32_t i = 0; i < _topPoints.size(); ++i) { - rayStart = newPosition + rotation * _topPoints[i] - backSlop * rayDirection; - rayEnd = rayStart + (backSlop + stepLength + forwardSlop) * rayDirection; - if (_ghost.rayTest(rayStart, rayEnd, rayResult)) { - if (rayResult.m_closestHitFraction < closestRayResult.m_closestHitFraction) { - closestRayResult = rayResult; - } - if (result.walkable) { - if (rayResult.m_hitNormalWorld.dot(_currentUp) < _minFloorNormalDotUp) { - result.walkable = false; - // the top scan wasn't walkable so don't bother scanning the bottom - // remove both forwardSlop and backSlop - result.hitFraction = glm::min(1.0f, (closestRayResult.m_closestHitFraction * (backSlop + stepLength + forwardSlop) - backSlop) / stepLength); - return result.hitFraction < 1.0f; - } - } - } - } - if (_state == State::Hover) { - // scan the bottom just like the top - for (int32_t i = 0; i < _bottomPoints.size(); ++i) { - rayStart = newPosition + rotation * _bottomPoints[i] - backSlop * rayDirection; - rayEnd = rayStart + (backSlop + stepLength + forwardSlop) * rayDirection; - if (_ghost.rayTest(rayStart, rayEnd, rayResult)) { - if (rayResult.m_closestHitFraction < closestRayResult.m_closestHitFraction) { - closestRayResult = rayResult; - } - if (result.walkable) { - if (rayResult.m_hitNormalWorld.dot(_currentUp) < _minFloorNormalDotUp) { - result.walkable = false; - // the bottom scan wasn't walkable - // remove both forwardSlop and backSlop - result.hitFraction = glm::min(1.0f, (closestRayResult.m_closestHitFraction * (backSlop + stepLength + forwardSlop) - backSlop) / stepLength); - return result.hitFraction < 1.0f; - } - } - } - } - } else { - // scan the bottom looking for nearest step point - // remove forwardSlop - result.hitFraction = (closestRayResult.m_closestHitFraction * (backSlop + stepLength + forwardSlop)) / (backSlop + stepLength); - - for (int32_t i = 0; i < _bottomPoints.size(); ++i) { - rayStart = newPosition + rotation * _bottomPoints[i] - backSlop * rayDirection; - rayEnd = rayStart + (backSlop + stepLength) * rayDirection; - if (_ghost.rayTest(rayStart, rayEnd, rayResult)) { - if (rayResult.m_closestHitFraction < closestRayResult.m_closestHitFraction) { - closestRayResult = rayResult; - } - } - } - // remove backSlop - // NOTE: backSlop removal can produce a NEGATIVE hitFraction! - // which means the shape is actually in interpenetration - result.hitFraction = ((closestRayResult.m_closestHitFraction * (backSlop + stepLength)) - backSlop) / stepLength; - } - return result.hitFraction < 1.0f; -} - -glm::vec3 MyCharacterController::computeHMDStep(const glm::vec3& position, const glm::vec3& step) { - btVector3 stepDirection = glmToBullet(step); - btScalar stepLength = stepDirection.length(); - if (stepLength < FLT_EPSILON) { - return glm::vec3(0.0f); - } - stepDirection /= stepLength; - - // get _ghost ready for ray traces - btTransform transform = _rigidBody->getWorldTransform(); - btVector3 newPosition = glmToBullet(position); - transform.setOrigin(newPosition); - btMatrix3x3 rotation = transform.getBasis(); - _ghost.setWorldTransform(transform); - _ghost.refreshOverlappingPairCache(); - - // compute rotation that will orient local ray start points to face stepDirection - btVector3 forward = rotation * btVector3(0.0f, 0.0f, -1.0f); - btVector3 horizontalDirection = stepDirection - stepDirection.dot(_currentUp) * _currentUp; - btVector3 axis = forward.cross(horizontalDirection); - btScalar lengthAxis = axis.length(); - if (lengthAxis > FLT_EPSILON) { - // non-zero sideways component - btScalar angle = asinf(lengthAxis / horizontalDirection.length()); - if (stepDirection.dot(forward) < 0.0f) { - angle = PI - angle; - } - axis /= lengthAxis; - rotation = btMatrix3x3(btQuaternion(axis, angle)) * rotation; - } else if (stepDirection.dot(forward) < 0.0f) { - // backwards - rotation = btMatrix3x3(btQuaternion(_currentUp, PI)) * rotation; - } - - CharacterRayResult rayResult(&_ghost); - btVector3 rayStart; - btVector3 rayEnd; - btVector3 penetration = btVector3(0.0f, 0.0f, 0.0f); - int32_t numPenetrations = 0; - - { // first we scan straight out from capsule center to see if we're stuck on anything - btScalar forwardRatio = 0.5f; - btScalar backRatio = 0.25f; - - btVector3 radial; - bool stuck = false; - for (int32_t i = 0; i < _topPoints.size(); ++i) { - rayStart = rotation * _topPoints[i]; - radial = rayStart - rayStart.dot(_currentUp) * _currentUp; - rayEnd = newPosition + rayStart + forwardRatio * radial; - rayStart += newPosition - backRatio * radial; - - // reset rayResult for next test - rayResult.m_closestHitFraction = 1.0f; - rayResult.m_collisionObject = nullptr; - - if (_ghost.rayTest(rayStart, rayEnd, rayResult)) { - btScalar totalRatio = backRatio + forwardRatio; - btScalar adjustedHitFraction = (rayResult.m_closestHitFraction * totalRatio - backRatio) / forwardRatio; - if (adjustedHitFraction < 0.0f) { - penetration += adjustedHitFraction * radial; - ++numPenetrations; - } else { - stuck = true; - } - } - } - if (numPenetrations > 0) { - if (numPenetrations > 1) { - penetration /= (btScalar)numPenetrations; - } - return bulletToGLM(penetration); - } else if (stuck) { - return glm::vec3(0.0f); - } - } - - // if we get here then we're not stuck pushing into any surface - // so now we scan to see if the way before us is "walkable" - - // scan the top - // NOTE: if we scan an extra distance forward we can detect flat surfaces that are too steep to walk on. - // The approximate extra distance can be derived with trigonometry. - // - // minimumForward = [ (maxStepHeight + radius / cosTheta - radius) * (cosTheta / sinTheta) - radius ] - // - // where: theta = max angle between floor normal and vertical - // - // if stepLength is not long enough we can add the difference. - // - btScalar cosTheta = _minFloorNormalDotUp; - btScalar sinTheta = sqrtf(1.0f - cosTheta * cosTheta); - const btScalar MIN_FORWARD_SLOP = 0.10f; // HACK: not sure why this is necessary to detect steepest walkable slope - btScalar forwardSlop = (_maxStepHeight + _radius / cosTheta - _radius) * (cosTheta / sinTheta) - (_radius + stepLength) + MIN_FORWARD_SLOP; - if (forwardSlop < 0.0f) { - // BIG step, no slop necessary - forwardSlop = 0.0f; - } - - // we push the step forward by stepMargin to help reduce accidental penetration - const btScalar MIN_STEP_MARGIN = 0.04f; - btScalar stepMargin = glm::max(_radius, MIN_STEP_MARGIN); - btScalar expandedStepLength = stepLength + forwardSlop + stepMargin; - - // loop over topPoints - bool walkable = true; - for (int32_t i = 0; i < _topPoints.size(); ++i) { - rayStart = newPosition + rotation * _topPoints[i]; - rayEnd = rayStart + expandedStepLength * stepDirection; - - // reset rayResult for next test - rayResult.m_closestHitFraction = 1.0f; - rayResult.m_collisionObject = nullptr; - - if (_ghost.rayTest(rayStart, rayEnd, rayResult)) { - if (rayResult.m_hitNormalWorld.dot(_currentUp) < _minFloorNormalDotUp) { - walkable = false; - break; - } - } - } - - // scan the bottom - // TODO: implement sliding along sloped floors - bool steppingUp = false; - expandedStepLength = stepLength + MIN_FORWARD_SLOP + MIN_STEP_MARGIN; - for (int32_t i = _bottomPoints.size() - 1; i > -1; --i) { - rayStart = newPosition + rotation * _bottomPoints[i] - MIN_STEP_MARGIN * stepDirection; - rayEnd = rayStart + expandedStepLength * stepDirection; - - // reset rayResult for next test - rayResult.m_closestHitFraction = 1.0f; - rayResult.m_collisionObject = nullptr; - - if (_ghost.rayTest(rayStart, rayEnd, rayResult)) { - btScalar adjustedHitFraction = (rayResult.m_closestHitFraction * expandedStepLength - MIN_STEP_MARGIN) / (stepLength + MIN_FORWARD_SLOP); - if (adjustedHitFraction < 1.0f) { - steppingUp = true; - break; - } - } - } - - if (!walkable && steppingUp ) { - return glm::vec3(0.0f); - } - // else it might not be walkable, but we aren't steppingUp yet which means we can still move forward - - // TODO: slide up ramps and fall off edges (then we can remove the vertical follow of Avatar's RigidBody) - return step; -} - -btConvexHullShape* MyCharacterController::computeShape() const { - // HACK: the avatar collides using convex hull with a collision margin equal to - // the old capsule radius. Two points define a capsule and additional points are - // spread out at chest level to produce a slight taper toward the feet. This - // makes the avatar more likely to collide with vertical walls at a higher point - // and thus less likely to produce a single-point collision manifold below the - // _maxStepHeight when walking into against vertical surfaces --> fixes a bug - // where the "walk up steps" feature would allow the avatar to walk up vertical - // walls. - const int32_t NUM_POINTS = 6; - btVector3 points[NUM_POINTS]; - btVector3 xAxis = btVector3(1.0f, 0.0f, 0.0f); - btVector3 yAxis = btVector3(0.0f, 1.0f, 0.0f); - btVector3 zAxis = btVector3(0.0f, 0.0f, 1.0f); - points[0] = _halfHeight * yAxis; - points[1] = -_halfHeight * yAxis; - points[2] = (0.75f * _halfHeight) * yAxis - (0.1f * _radius) * zAxis; - points[3] = (0.75f * _halfHeight) * yAxis + (0.1f * _radius) * zAxis; - points[4] = (0.75f * _halfHeight) * yAxis - (0.1f * _radius) * xAxis; - points[5] = (0.75f * _halfHeight) * yAxis + (0.1f * _radius) * xAxis; - btConvexHullShape* shape = new btConvexHullShape(reinterpret_cast(points), NUM_POINTS); - shape->setMargin(_radius); - return shape; -} - -void MyCharacterController::initRayShotgun(const btCollisionWorld* world) { - // In order to trace rays out from the avatar's shape surface we need to know where the start points are in - // the local-frame. Since the avatar shape is somewhat irregular computing these points by hand is a hassle - // so instead we ray-trace backwards to the avatar to find them. - // - // We trace back a regular grid (see below) of points against the shape and keep any that hit. - // ___ - // + / + \ + - // |+ +| - // +| + | + - // |+ +| - // +| + | + - // |+ +| - // + \ + / + - // --- - // The shotgun will send rays out from these same points to see if the avatar's shape can proceed through space. - - // helper class for simple ray-traces against character - class MeOnlyResultCallback : public btCollisionWorld::ClosestRayResultCallback { - public: - MeOnlyResultCallback (btRigidBody* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0f, 0.0f, 0.0f), btVector3(0.0f, 0.0f, 0.0f)) { - _me = me; - m_collisionFilterGroup = BULLET_COLLISION_GROUP_DYNAMIC; - m_collisionFilterMask = BULLET_COLLISION_MASK_DYNAMIC; - } - virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace) override { - if (rayResult.m_collisionObject != _me) { - return 1.0f; - } - return ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace); - } - btRigidBody* _me; - }; - - const btScalar fullHalfHeight = _radius + _halfHeight; - const btScalar divisionLine = -fullHalfHeight + _maxStepHeight; // line between top and bottom - const btScalar topHeight = fullHalfHeight - divisionLine; - const btScalar slop = 0.02f; - - const int32_t NUM_ROWS = 5; // must be odd number > 1 - const int32_t NUM_COLUMNS = 5; // must be odd number > 1 - btVector3 reach = (2.0f * _radius) * btVector3(0.0f, 0.0f, 1.0f); - - { // top points - _topPoints.clear(); - _topPoints.reserve(NUM_ROWS * NUM_COLUMNS); - btScalar stepY = (topHeight - slop) / (btScalar)(NUM_ROWS - 1); - btScalar stepX = 2.0f * (_radius - slop) / (btScalar)(NUM_COLUMNS - 1); - - btTransform transform = _rigidBody->getWorldTransform(); - btVector3 position = transform.getOrigin(); - btMatrix3x3 rotation = transform.getBasis(); - - for (int32_t i = 0; i < NUM_ROWS; ++i) { - int32_t maxJ = NUM_COLUMNS; - btScalar offsetX = -(btScalar)((NUM_COLUMNS - 1) / 2) * stepX; - if (i % 2 == 1) { - // odd rows have one less point and start a halfStep closer - maxJ -= 1; - offsetX += 0.5f * stepX; - } - for (int32_t j = 0; j < maxJ; ++j) { - btVector3 localRayEnd(offsetX + (btScalar)(j) * stepX, divisionLine + (btScalar)(i) * stepY, 0.0f); - btVector3 localRayStart = localRayEnd - reach; - MeOnlyResultCallback result(_rigidBody); - world->rayTest(position + rotation * localRayStart, position + rotation * localRayEnd, result); - if (result.m_closestHitFraction < 1.0f) { - _topPoints.push_back(localRayStart + result.m_closestHitFraction * reach); - } - } - } - } - - { // bottom points - _bottomPoints.clear(); - _bottomPoints.reserve(NUM_ROWS * NUM_COLUMNS); - - btScalar steepestStepHitHeight = (_radius + 0.04f) * (1.0f - DEFAULT_MIN_FLOOR_NORMAL_DOT_UP); - btScalar stepY = (_maxStepHeight - slop - steepestStepHitHeight) / (btScalar)(NUM_ROWS - 1); - btScalar stepX = 2.0f * (_radius - slop) / (btScalar)(NUM_COLUMNS - 1); - - btTransform transform = _rigidBody->getWorldTransform(); - btVector3 position = transform.getOrigin(); - btMatrix3x3 rotation = transform.getBasis(); - - for (int32_t i = 0; i < NUM_ROWS; ++i) { - int32_t maxJ = NUM_COLUMNS; - btScalar offsetX = -(btScalar)((NUM_COLUMNS - 1) / 2) * stepX; - if (i % 2 == 1) { - // odd rows have one less point and start a halfStep closer - maxJ -= 1; - offsetX += 0.5f * stepX; - } - for (int32_t j = 0; j < maxJ; ++j) { - btVector3 localRayEnd(offsetX + (btScalar)(j) * stepX, (divisionLine - slop) - (btScalar)(i) * stepY, 0.0f); - btVector3 localRayStart = localRayEnd - reach; - MeOnlyResultCallback result(_rigidBody); - world->rayTest(position + rotation * localRayStart, position + rotation * localRayEnd, result); - if (result.m_closestHitFraction < 1.0f) { - _bottomPoints.push_back(localRayStart + result.m_closestHitFraction * reach); - } - } - } - } -} diff --git a/interface/src/avatar/MyCharacterController.h b/interface/src/avatar/MyCharacterController.h index 4c465185d6..265406bc6f 100644 --- a/interface/src/avatar/MyCharacterController.h +++ b/interface/src/avatar/MyCharacterController.h @@ -24,37 +24,10 @@ public: explicit MyCharacterController(MyAvatar* avatar); ~MyCharacterController (); - void setDynamicsWorld(btDynamicsWorld* world) override; - void updateShapeIfNecessary() override; - - // Sweeping a convex shape through the physics simulation can expensive when the obstacles are too complex - // (e.g. small 20k triangle static mesh) so instead as a fallback we cast several rays forward and if they - // don't hit anything we consider it a clean sweep. Hence the "Shotgun" code. - class RayShotgunResult { - public: - void reset(); - - float hitFraction { 1.0f }; - bool walkable { true }; - }; - - /// return true if RayShotgun hits anything - bool testRayShotgun(const glm::vec3& position, const glm::vec3& step, RayShotgunResult& result); - - glm::vec3 computeHMDStep(const glm::vec3& position, const glm::vec3& step); - -protected: - void initRayShotgun(const btCollisionWorld* world); - -private: - btConvexHullShape* computeShape() const; + virtual void updateShapeIfNecessary() override; protected: MyAvatar* _avatar { nullptr }; - - // shotgun scan data - btAlignedObjectArray _topPoints; - btAlignedObjectArray _bottomPoints; }; #endif // hifi_MyCharacterController_h diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index b1e0f31062..889f0ef36b 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -23,12 +23,6 @@ #include "InterfaceLogging.h" #include "AnimDebugDraw.h" -glm::vec3 TRUNCATE_IK_CAPSULE_POSITION(0.0f, 0.0f, 0.0f); -float TRUNCATE_IK_CAPSULE_LENGTH = 1000.0f; -float TRUNCATE_IK_CAPSULE_RADIUS = 0.25f; -float MIN_OUT_OF_BODY_DISTANCE = TRUNCATE_IK_CAPSULE_RADIUS - 0.1f; -float MAX_OUT_OF_BODY_DISTANCE = TRUNCATE_IK_CAPSULE_RADIUS + 0.1f; - SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) : Model(rig, parent), _owningAvatar(owningAvatar), @@ -92,6 +86,7 @@ Rig::CharacterControllerState convertCharacterControllerState(CharacterControlle }; } + // Called within Model::simulate call, below. void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { const FBXGeometry& geometry = getFBXGeometry(); @@ -112,9 +107,6 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { Rig::HeadParameters headParams; - glm::vec3 hmdPositionInRigSpace; - glm::vec3 truncatedHMDPositionInRigSpace; - if (qApp->isHMDMode()) { headParams.isInHMD = true; @@ -124,22 +116,9 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { glm::mat4 worldToRig = glm::inverse(rigToWorld); glm::mat4 rigHMDMat = worldToRig * worldHMDMat; - hmdPositionInRigSpace = extractTranslation(rigHMDMat); - - glm::vec3 capsuleStart = Vectors::UNIT_Y * (TRUNCATE_IK_CAPSULE_LENGTH / 2.0f); - glm::vec3 capsuleEnd = -Vectors::UNIT_Y * (TRUNCATE_IK_CAPSULE_LENGTH / 2.0f); - - // truncate head IK target if it's out of body - if (myAvatar->isOutOfBody()) { - truncatedHMDPositionInRigSpace = projectPointOntoCapsule(hmdPositionInRigSpace, capsuleStart, capsuleEnd, TRUNCATE_IK_CAPSULE_RADIUS); - } else { - truncatedHMDPositionInRigSpace = hmdPositionInRigSpace; - } - - headParams.rigHeadPosition = truncatedHMDPositionInRigSpace; + headParams.rigHeadPosition = extractTranslation(rigHMDMat); headParams.rigHeadOrientation = extractRotation(rigHMDMat); headParams.worldHeadOrientation = extractRotation(worldHMDMat); - } else { headParams.isInHMD = false; @@ -153,42 +132,13 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig->updateFromHeadParameters(headParams, deltaTime); - // OUTOFBODY_HACK: clamp horizontal component of head by maxHipsOffset. - // This is to prevent the hands from being incorrect relative to the head because - // the hips are being constrained by a small maxHipsOffset due to collision. - if (myAvatar->isOutOfBody()) { - float headOffsetLength2D = glm::length(glm::vec2(truncatedHMDPositionInRigSpace.x, truncatedHMDPositionInRigSpace.z)); - if (headOffsetLength2D > _rig->getMaxHipsOffsetLength()) { - truncatedHMDPositionInRigSpace.x *= _rig->getMaxHipsOffsetLength() / headOffsetLength2D; - truncatedHMDPositionInRigSpace.z *= _rig->getMaxHipsOffsetLength() / headOffsetLength2D; - } - } - Rig::HandParameters handParams; - // compute interp factor between in body and out of body hand positions. - glm::vec3 capsuleStart = Vectors::UNIT_Y * (TRUNCATE_IK_CAPSULE_LENGTH / 2.0f); - glm::vec3 capsuleEnd = -Vectors::UNIT_Y * (TRUNCATE_IK_CAPSULE_LENGTH / 2.0f); - float outOfBodyAlpha = distanceFromCapsule(hmdPositionInRigSpace, capsuleStart, capsuleEnd, TRUNCATE_IK_CAPSULE_RADIUS); - outOfBodyAlpha = (glm::clamp(outOfBodyAlpha, MIN_OUT_OF_BODY_DISTANCE, MAX_OUT_OF_BODY_DISTANCE) - MIN_OUT_OF_BODY_DISTANCE) / - (MAX_OUT_OF_BODY_DISTANCE - MIN_OUT_OF_BODY_DISTANCE); - auto leftPose = myAvatar->getLeftHandControllerPoseInAvatarFrame(); if (leftPose.isValid()) { handParams.isLeftEnabled = true; handParams.leftPosition = Quaternions::Y_180 * leftPose.getTranslation(); handParams.leftOrientation = Quaternions::Y_180 * leftPose.getRotation(); - - // adjust hand position if head is out of body. - if (qApp->isHMDMode()) { - - // compute the out of body hand position. - glm::vec3 offset = handParams.leftPosition - hmdPositionInRigSpace; - glm::vec3 outOfBodyLeftPosition = truncatedHMDPositionInRigSpace + offset; - - // interpolate between in body and out of body hand position. - handParams.leftPosition = lerp(handParams.leftPosition, outOfBodyLeftPosition, outOfBodyAlpha); - } } else { handParams.isLeftEnabled = false; } @@ -198,17 +148,6 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { handParams.isRightEnabled = true; handParams.rightPosition = Quaternions::Y_180 * rightPose.getTranslation(); handParams.rightOrientation = Quaternions::Y_180 * rightPose.getRotation(); - - // adjust hand position if head is out of body. - if (qApp->isHMDMode()) { - - // compute the out of body hand position. - glm::vec3 offset = handParams.rightPosition - hmdPositionInRigSpace; - glm::vec3 outOfBodyRightPosition = truncatedHMDPositionInRigSpace + offset; - - // interpolate between in body and out of body hand position. - handParams.rightPosition = lerp(handParams.rightPosition, outOfBodyRightPosition, outOfBodyAlpha); - } } else { handParams.isRightEnabled = false; } @@ -221,7 +160,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { Rig::CharacterControllerState ccState = convertCharacterControllerState(myAvatar->getCharacterController()->getState()); - auto velocity = myAvatar->getPreActionVelocity(); + auto velocity = myAvatar->getLocalVelocity(); auto position = myAvatar->getLocalPosition(); auto orientation = myAvatar->getLocalOrientation(); _rig->computeMotionAnimationState(deltaTime, position, velocity, orientation, ccState); diff --git a/interface/src/scripting/HMDScriptingInterface.cpp b/interface/src/scripting/HMDScriptingInterface.cpp index 243479428e..fb1440ebdf 100644 --- a/interface/src/scripting/HMDScriptingInterface.cpp +++ b/interface/src/scripting/HMDScriptingInterface.cpp @@ -114,21 +114,6 @@ glm::vec3 HMDScriptingInterface::getPosition() const { return glm::vec3(); } -void HMDScriptingInterface::setPosition(const glm::vec3& position) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setPosition", Qt::QueuedConnection, Q_ARG(const glm::vec3&, position)); - return; - } else { - auto myAvatar = DependencyManager::get()->getMyAvatar(); - glm::mat4 hmdToSensor = myAvatar->getHMDSensorMatrix(); - glm::mat4 sensorToWorld = myAvatar->getSensorToWorldMatrix(); - glm::mat4 hmdToWorld = sensorToWorld * hmdToSensor; - setTranslation(hmdToWorld, position); - sensorToWorld = hmdToWorld * glm::inverse(hmdToSensor); - myAvatar->setSensorToWorldMatrix(sensorToWorld); - } -} - glm::quat HMDScriptingInterface::getOrientation() const { if (qApp->getActiveDisplayPlugin()->isHmd()) { return glm::normalize(glm::quat_cast(getWorldHMDMatrix())); @@ -199,8 +184,3 @@ bool HMDScriptingInterface::isKeyboardVisible() { void HMDScriptingInterface::centerUI() { QMetaObject::invokeMethod(qApp, "centerUI", Qt::QueuedConnection); } - -void HMDScriptingInterface::snapToAvatar() { - auto myAvatar = DependencyManager::get()->getMyAvatar(); - myAvatar->updateSensorToWorldMatrix(); -} diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h index f6b75ef625..c9ed7f0097 100644 --- a/interface/src/scripting/HMDScriptingInterface.h +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -25,7 +25,7 @@ class QScriptEngine; class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Dependency { Q_OBJECT - Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition) + Q_PROPERTY(glm::vec3 position READ getPosition) Q_PROPERTY(glm::quat orientation READ getOrientation) Q_PROPERTY(bool mounted READ isMounted) @@ -56,7 +56,7 @@ public: /// not be interrupted by a keyboard popup /// Returns false if there is already an active keyboard displayed. /// Clients should re-enable the keyboard when the operation is complete and ensure - /// that they balance any call to suppressKeyboard() that returns true with a corresponding + /// that they balance any call to suppressKeyboard() that returns true with a corresponding /// call to unsuppressKeyboard() within a reasonable amount of time Q_INVOKABLE bool suppressKeyboard(); @@ -69,9 +69,6 @@ public: // rotate the overlay UI sphere so that it is centered about the the current HMD position and orientation Q_INVOKABLE void centerUI(); - // snap HMD to align with Avatar's current position in world-frame - Q_INVOKABLE void snapToAvatar(); - signals: bool shouldShowHandControllersChanged(); @@ -85,10 +82,7 @@ public: private: // Get the position of the HMD glm::vec3 getPosition() const; - - // Set the position of the HMD - Q_INVOKABLE void setPosition(const glm::vec3& position); - + // Get the orientation of the HMD glm::quat getOrientation() const; diff --git a/libraries/animation/src/AnimBlendLinear.cpp b/libraries/animation/src/AnimBlendLinear.cpp index 936126bf52..52c440a14e 100644 --- a/libraries/animation/src/AnimBlendLinear.cpp +++ b/libraries/animation/src/AnimBlendLinear.cpp @@ -24,7 +24,7 @@ AnimBlendLinear::~AnimBlendLinear() { } -const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) { +const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { _alpha = animVars.lookup(_alphaVar, _alpha); @@ -33,7 +33,7 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, con pose = AnimPose::identity; } } else if (_children.size() == 1) { - _poses = _children[0]->evaluate(animVars, context, dt, triggersOut); + _poses = _children[0]->evaluate(animVars, dt, triggersOut); } else { float clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1)); @@ -41,7 +41,7 @@ const AnimPoseVec& AnimBlendLinear::evaluate(const AnimVariantMap& animVars, con size_t nextPoseIndex = glm::ceil(clampedAlpha); float alpha = glm::fract(clampedAlpha); - evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, dt); + evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, dt); } return _poses; } @@ -51,15 +51,15 @@ const AnimPoseVec& AnimBlendLinear::getPosesInternal() const { return _poses; } -void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, Triggers& triggersOut, float alpha, +void AnimBlendLinear::evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, size_t prevPoseIndex, size_t nextPoseIndex, float dt) { if (prevPoseIndex == nextPoseIndex) { // this can happen if alpha is on an integer boundary - _poses = _children[prevPoseIndex]->evaluate(animVars, context, dt, triggersOut); + _poses = _children[prevPoseIndex]->evaluate(animVars, dt, triggersOut); } else { // need to eval and blend between two children. - auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, context, dt, triggersOut); - auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, context, dt, triggersOut); + auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, dt, triggersOut); + auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, dt, triggersOut); if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) { _poses.resize(prevPoses.size()); diff --git a/libraries/animation/src/AnimBlendLinear.h b/libraries/animation/src/AnimBlendLinear.h index 0dae6aabdb..2478f9b473 100644 --- a/libraries/animation/src/AnimBlendLinear.h +++ b/libraries/animation/src/AnimBlendLinear.h @@ -30,7 +30,7 @@ public: AnimBlendLinear(const QString& id, float alpha); virtual ~AnimBlendLinear() override; - virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override; + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; } @@ -38,7 +38,7 @@ protected: // for AnimDebugDraw rendering virtual const AnimPoseVec& getPosesInternal() const override; - void evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, Triggers& triggersOut, float alpha, + void evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, size_t prevPoseIndex, size_t nextPoseIndex, float dt); AnimPoseVec _poses; diff --git a/libraries/animation/src/AnimBlendLinearMove.cpp b/libraries/animation/src/AnimBlendLinearMove.cpp index 40fbb5a6f7..609b464512 100644 --- a/libraries/animation/src/AnimBlendLinearMove.cpp +++ b/libraries/animation/src/AnimBlendLinearMove.cpp @@ -26,7 +26,7 @@ AnimBlendLinearMove::~AnimBlendLinearMove() { } -const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) { +const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { assert(_children.size() == _characteristicSpeeds.size()); @@ -43,7 +43,7 @@ const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars, const int nextPoseIndex = 0; float prevDeltaTime, nextDeltaTime; setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut); - evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime); + evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime); } else { auto clampedAlpha = glm::clamp(_alpha, 0.0f, (float)(_children.size() - 1)); @@ -52,7 +52,7 @@ const AnimPoseVec& AnimBlendLinearMove::evaluate(const AnimVariantMap& animVars, auto alpha = glm::fract(clampedAlpha); float prevDeltaTime, nextDeltaTime; setFrameAndPhase(dt, alpha, prevPoseIndex, nextPoseIndex, &prevDeltaTime, &nextDeltaTime, triggersOut); - evaluateAndBlendChildren(animVars, context, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime); + evaluateAndBlendChildren(animVars, triggersOut, alpha, prevPoseIndex, nextPoseIndex, prevDeltaTime, nextDeltaTime); } return _poses; } @@ -62,16 +62,16 @@ const AnimPoseVec& AnimBlendLinearMove::getPosesInternal() const { return _poses; } -void AnimBlendLinearMove::evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, Triggers& triggersOut, float alpha, +void AnimBlendLinearMove::evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, size_t prevPoseIndex, size_t nextPoseIndex, float prevDeltaTime, float nextDeltaTime) { if (prevPoseIndex == nextPoseIndex) { // this can happen if alpha is on an integer boundary - _poses = _children[prevPoseIndex]->evaluate(animVars, context, prevDeltaTime, triggersOut); + _poses = _children[prevPoseIndex]->evaluate(animVars, prevDeltaTime, triggersOut); } else { // need to eval and blend between two children. - auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, context, prevDeltaTime, triggersOut); - auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, context, nextDeltaTime, triggersOut); + auto prevPoses = _children[prevPoseIndex]->evaluate(animVars, prevDeltaTime, triggersOut); + auto nextPoses = _children[nextPoseIndex]->evaluate(animVars, nextDeltaTime, triggersOut); if (prevPoses.size() > 0 && prevPoses.size() == nextPoses.size()) { _poses.resize(prevPoses.size()); diff --git a/libraries/animation/src/AnimBlendLinearMove.h b/libraries/animation/src/AnimBlendLinearMove.h index 083858f873..4e04ce29cb 100644 --- a/libraries/animation/src/AnimBlendLinearMove.h +++ b/libraries/animation/src/AnimBlendLinearMove.h @@ -39,7 +39,7 @@ public: AnimBlendLinearMove(const QString& id, float alpha, float desiredSpeed, const std::vector& characteristicSpeeds); virtual ~AnimBlendLinearMove() override; - virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override; + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; } void setDesiredSpeedVar(const QString& desiredSpeedVar) { _desiredSpeedVar = desiredSpeedVar; } @@ -48,7 +48,7 @@ protected: // for AnimDebugDraw rendering virtual const AnimPoseVec& getPosesInternal() const override; - void evaluateAndBlendChildren(const AnimVariantMap& animVars, const AnimContext& context, Triggers& triggersOut, float alpha, + void evaluateAndBlendChildren(const AnimVariantMap& animVars, Triggers& triggersOut, float alpha, size_t prevPoseIndex, size_t nextPoseIndex, float prevDeltaTime, float nextDeltaTime); diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 273b83743d..a5747e4f96 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -31,7 +31,7 @@ AnimClip::~AnimClip() { } -const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) { +const AnimPoseVec& AnimClip::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { // lookup parameters from animVars, using current instance variables as defaults. _startFrame = animVars.lookup(_startFrameVar, _startFrame); diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index c7e7ebf3ee..7989f6d172 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -30,7 +30,7 @@ public: AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag); virtual ~AnimClip() override; - virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override; + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; void setStartFrameVar(const QString& startFrameVar) { _startFrameVar = startFrameVar; } void setEndFrameVar(const QString& endFrameVar) { _endFrameVar = endFrameVar; } diff --git a/libraries/animation/src/AnimContext.cpp b/libraries/animation/src/AnimContext.cpp deleted file mode 100644 index c8d3e7bcda..0000000000 --- a/libraries/animation/src/AnimContext.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// -// AnimContext.cpp -// -// Created by Anthony J. Thibault on 9/19/16. -// Copyright (c) 2016 High Fidelity, Inc. All rights reserved. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "AnimContext.h" - -AnimContext::AnimContext(bool enableDebugDrawIKTargets, const glm::mat4& geometryToRigMatrix) : - _enableDebugDrawIKTargets(enableDebugDrawIKTargets), - _geometryToRigMatrix(geometryToRigMatrix) { -} diff --git a/libraries/animation/src/AnimContext.h b/libraries/animation/src/AnimContext.h deleted file mode 100644 index 3170911e14..0000000000 --- a/libraries/animation/src/AnimContext.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// AnimContext.h -// -// Created by Anthony J. Thibault on 9/19/16. -// Copyright (c) 2016 High Fidelity, Inc. All rights reserved. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_AnimContext_h -#define hifi_AnimContext_h - -#include -#include - -class AnimContext { -public: - AnimContext(bool enableDebugDrawIKTargets, const glm::mat4& geometryToRigMatrix); - - bool getEnableDebugDrawIKTargets() const { return _enableDebugDrawIKTargets; } - const glm::mat4& getGeometryToRigMatrix() const { return _geometryToRigMatrix; } - -protected: - - bool _enableDebugDrawIKTargets { false }; - glm::mat4 _geometryToRigMatrix; -}; - -#endif // hifi_AnimContext_h diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index 4dca0aa6a8..7985251002 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -14,8 +14,6 @@ #include #include #include -#include -#include "Rig.h" #include "ElbowConstraint.h" #include "SwingTwistConstraint.h" @@ -378,14 +376,14 @@ int AnimInverseKinematics::solveTargetWithCCD(const IKTarget& target, AnimPoseVe } //virtual -const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimNode::Triggers& triggersOut) { +const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVars, float dt, AnimNode::Triggers& triggersOut) { // don't call this function, call overlay() instead assert(false); return _relativePoses; } //virtual -const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) { +const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) { const float MAX_OVERLAY_DT = 1.0f / 30.0f; // what to clamp delta-time to in AnimInverseKinematics::overlay if (dt > MAX_OVERLAY_DT) { @@ -439,28 +437,6 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars computeTargets(animVars, targets, underPoses); } - // debug render ik targets - if (context.getEnableDebugDrawIKTargets()) { - const vec4 WHITE(1.0f); - glm::mat4 rigToAvatarMat = createMatFromQuatAndPos(Quaternions::Y_180, glm::vec3()); - - for (auto& target : targets) { - glm::mat4 geomTargetMat = createMatFromQuatAndPos(target.getRotation(), target.getTranslation()); - glm::mat4 avatarTargetMat = rigToAvatarMat * context.getGeometryToRigMatrix() * geomTargetMat; - - std::string name = "ikTarget" + std::to_string(target.getIndex()); - DebugDraw::getInstance().addMyAvatarMarker(name, glmExtractRotation(avatarTargetMat), extractTranslation(avatarTargetMat), WHITE); - } - } else if (context.getEnableDebugDrawIKTargets() != _previousEnableDebugIKTargets) { - // remove markers if they were added last frame. - for (auto& target : targets) { - std::string name = "ikTarget" + std::to_string(target.getIndex()); - DebugDraw::getInstance().removeMyAvatarMarker(name); - } - } - - _previousEnableDebugIKTargets = context.getEnableDebugDrawIKTargets(); - if (targets.empty()) { // no IK targets but still need to enforce constraints std::map::iterator constraintItr = _constraints.begin(); @@ -510,13 +486,7 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars // measure new _hipsOffset for next frame // by looking for discrepancies between where a targeted endEffector is // and where it wants to be (after IK solutions are done) - - // OUTOFBODY_HACK:use weighted average between HMD and other targets - float HMD_WEIGHT = 10.0f; - float OTHER_WEIGHT = 1.0f; - float totalWeight = 0.0f; - - glm::vec3 additionalHipsOffset = Vectors::ZERO; + glm::vec3 newHipsOffset = Vectors::ZERO; for (auto& target: targets) { int targetIndex = target.getIndex(); if (targetIndex == _headIndex && _headIndex != -1) { @@ -527,45 +497,26 @@ const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars glm::vec3 under = _skeleton->getAbsolutePose(_headIndex, underPoses).trans; glm::vec3 actual = _skeleton->getAbsolutePose(_headIndex, _relativePoses).trans; const float HEAD_OFFSET_SLAVE_FACTOR = 0.65f; - additionalHipsOffset += (OTHER_WEIGHT * HEAD_OFFSET_SLAVE_FACTOR) * (under- actual); - totalWeight += OTHER_WEIGHT; + newHipsOffset += HEAD_OFFSET_SLAVE_FACTOR * (actual - under); } else if (target.getType() == IKTarget::Type::HmdHead) { + // we want to shift the hips to bring the head to its designated position glm::vec3 actual = _skeleton->getAbsolutePose(_headIndex, _relativePoses).trans; - glm::vec3 thisOffset = target.getTranslation() - actual; - glm::vec3 futureHipsOffset = _hipsOffset + thisOffset; - if (glm::length(glm::vec2(futureHipsOffset.x, futureHipsOffset.z)) < _maxHipsOffsetLength) { - // it is imperative to shift the hips and bring the head to its designated position - // so we slam newHipsOffset here and ignore all other targets - additionalHipsOffset = futureHipsOffset - _hipsOffset; - totalWeight = 0.0f; - break; - } else { - additionalHipsOffset += HMD_WEIGHT * (target.getTranslation() - actual); - totalWeight += HMD_WEIGHT; - } + _hipsOffset += target.getTranslation() - actual; + // and ignore all other targets + newHipsOffset = _hipsOffset; + break; } } else if (target.getType() == IKTarget::Type::RotationAndPosition) { glm::vec3 actualPosition = _skeleton->getAbsolutePose(targetIndex, _relativePoses).trans; glm::vec3 targetPosition = target.getTranslation(); - additionalHipsOffset += OTHER_WEIGHT * (targetPosition - actualPosition); - totalWeight += OTHER_WEIGHT; + newHipsOffset += targetPosition - actualPosition; } } - if (totalWeight > 1.0f) { - additionalHipsOffset /= totalWeight; - } // smooth transitions by relaxing _hipsOffset toward the new value - const float HIPS_OFFSET_SLAVE_TIMESCALE = 0.10f; + const float HIPS_OFFSET_SLAVE_TIMESCALE = 0.15f; float tau = dt < HIPS_OFFSET_SLAVE_TIMESCALE ? dt / HIPS_OFFSET_SLAVE_TIMESCALE : 1.0f; - _hipsOffset += additionalHipsOffset * tau; - - // clamp the horizontal component of the hips offset - float hipsOffsetLength2D = glm::length(glm::vec2(_hipsOffset.x, _hipsOffset.z)); - if (hipsOffsetLength2D > _maxHipsOffsetLength) { - _hipsOffset.x *= _maxHipsOffsetLength / hipsOffsetLength2D; - _hipsOffset.z *= _maxHipsOffsetLength / hipsOffsetLength2D; - } + _hipsOffset += (newHipsOffset - _hipsOffset) * tau; } } } @@ -578,12 +529,6 @@ void AnimInverseKinematics::clearIKJointLimitHistory() { } } -void AnimInverseKinematics::setMaxHipsOffsetLength(float maxLength) { - // OUTOFBODY_HACK: manually adjust scale here - const float METERS_TO_CENTIMETERS = 100.0f; - _maxHipsOffsetLength = METERS_TO_CENTIMETERS * maxLength; -} - RotationConstraint* AnimInverseKinematics::getConstraint(int index) { RotationConstraint* constraint = nullptr; std::map::iterator constraintItr = _constraints.find(index); diff --git a/libraries/animation/src/AnimInverseKinematics.h b/libraries/animation/src/AnimInverseKinematics.h index b61343a97b..c9560c7383 100644 --- a/libraries/animation/src/AnimInverseKinematics.h +++ b/libraries/animation/src/AnimInverseKinematics.h @@ -34,13 +34,11 @@ public: void setTargetVars(const QString& jointName, const QString& positionVar, const QString& rotationVar, const QString& typeVar); - virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimNode::Triggers& triggersOut) override; - virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override; + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, AnimNode::Triggers& triggersOut) override; + virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override; void clearIKJointLimitHistory(); - void setMaxHipsOffsetLength(float maxLength); - protected: void computeTargets(const AnimVariantMap& animVars, std::vector& targets, const AnimPoseVec& underPoses); void solveWithCyclicCoordinateDescent(const std::vector& targets); @@ -85,7 +83,6 @@ protected: // experimental data for moving hips during IK glm::vec3 _hipsOffset { Vectors::ZERO }; - float _maxHipsOffsetLength { 1.0f }; int _headIndex { -1 }; int _hipsIndex { -1 }; int _hipsParentIndex { -1 }; @@ -93,8 +90,6 @@ protected: // _maxTargetIndex is tracked to help optimize the recalculation of absolute poses // during the the cyclic coordinate descent algorithm int _maxTargetIndex { 0 }; - - bool _previousEnableDebugIKTargets { false }; }; #endif // hifi_AnimInverseKinematics_h diff --git a/libraries/animation/src/AnimManipulator.cpp b/libraries/animation/src/AnimManipulator.cpp index 37e239f3e1..3eedec5dbd 100644 --- a/libraries/animation/src/AnimManipulator.cpp +++ b/libraries/animation/src/AnimManipulator.cpp @@ -22,11 +22,11 @@ AnimManipulator::~AnimManipulator() { } -const AnimPoseVec& AnimManipulator::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) { - return overlay(animVars, context, dt, triggersOut, _skeleton->getRelativeBindPoses()); +const AnimPoseVec& AnimManipulator::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { + return overlay(animVars, dt, triggersOut, _skeleton->getRelativeBindPoses()); } -const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) { +const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) { _alpha = animVars.lookup(_alphaVar, _alpha); _poses = underPoses; diff --git a/libraries/animation/src/AnimManipulator.h b/libraries/animation/src/AnimManipulator.h index 26f50a7dd9..8534b9c269 100644 --- a/libraries/animation/src/AnimManipulator.h +++ b/libraries/animation/src/AnimManipulator.h @@ -22,8 +22,8 @@ public: AnimManipulator(const QString& id, float alpha); virtual ~AnimManipulator() override; - virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override; - virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override; + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; + virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override; void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; } diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index 10db38f42e..23f2e1c7b3 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -20,7 +20,6 @@ #include "AnimSkeleton.h" #include "AnimVariant.h" -#include "AnimContext.h" class QJsonObject; @@ -73,10 +72,9 @@ public: AnimSkeleton::ConstPointer getSkeleton() const { return _skeleton; } - virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) = 0; - virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, - const AnimPoseVec& underPoses) { - return evaluate(animVars, context, dt, triggersOut); + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) = 0; + virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) { + return evaluate(animVars, dt, triggersOut); } void setCurrentFrame(float frame); diff --git a/libraries/animation/src/AnimOverlay.cpp b/libraries/animation/src/AnimOverlay.cpp index dbc635af66..8f60b972ce 100644 --- a/libraries/animation/src/AnimOverlay.cpp +++ b/libraries/animation/src/AnimOverlay.cpp @@ -39,7 +39,7 @@ void AnimOverlay::buildBoneSet(BoneSet boneSet) { } } -const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) { +const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { // lookup parameters from animVars, using current instance variables as defaults. // NOTE: switching bonesets can be an expensive operation, let's try to avoid it. @@ -51,8 +51,8 @@ const AnimPoseVec& AnimOverlay::evaluate(const AnimVariantMap& animVars, const A _alpha = animVars.lookup(_alphaVar, _alpha); if (_children.size() >= 2) { - auto& underPoses = _children[1]->evaluate(animVars, context, dt, triggersOut); - auto& overPoses = _children[0]->overlay(animVars, context, dt, triggersOut, underPoses); + auto& underPoses = _children[1]->evaluate(animVars, dt, triggersOut); + auto& overPoses = _children[0]->overlay(animVars, dt, triggersOut, underPoses); if (underPoses.size() > 0 && underPoses.size() == overPoses.size()) { _poses.resize(underPoses.size()); diff --git a/libraries/animation/src/AnimOverlay.h b/libraries/animation/src/AnimOverlay.h index 2f34c07309..eda8847d40 100644 --- a/libraries/animation/src/AnimOverlay.h +++ b/libraries/animation/src/AnimOverlay.h @@ -43,7 +43,7 @@ public: AnimOverlay(const QString& id, BoneSet boneSet, float alpha); virtual ~AnimOverlay() override; - virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override; + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; void setBoneSetVar(const QString& boneSetVar) { _boneSetVar = boneSetVar; } void setAlphaVar(const QString& alphaVar) { _alphaVar = alphaVar; } diff --git a/libraries/animation/src/AnimStateMachine.cpp b/libraries/animation/src/AnimStateMachine.cpp index 4e86b92c0b..41d8a94b0a 100644 --- a/libraries/animation/src/AnimStateMachine.cpp +++ b/libraries/animation/src/AnimStateMachine.cpp @@ -21,7 +21,7 @@ AnimStateMachine::~AnimStateMachine() { } -const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) { +const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) { QString desiredStateID = animVars.lookup(_currentStateVar, _currentState->getID()); if (_currentState->getID() != desiredStateID) { @@ -29,7 +29,7 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co bool foundState = false; for (auto& state : _states) { if (state->getID() == desiredStateID) { - switchState(animVars, context, state); + switchState(animVars, state); foundState = true; break; } @@ -42,7 +42,7 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co // evaluate currentState transitions auto desiredState = evaluateTransitions(animVars); if (desiredState != _currentState) { - switchState(animVars, context, desiredState); + switchState(animVars, desiredState); } assert(_currentState); @@ -62,7 +62,7 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co } else if (_interpType == InterpType::SnapshotPrev) { // interp between the prev snapshot and evaluated next target. // this is useful for interping into a blend - localNextPoses = currentStateNode->evaluate(animVars, context, dt, triggersOut); + localNextPoses = currentStateNode->evaluate(animVars, dt, triggersOut); prevPoses = &_prevPoses; nextPoses = &localNextPoses; } else { @@ -79,7 +79,7 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, co } } if (!_duringInterp) { - _poses = currentStateNode->evaluate(animVars, context, dt, triggersOut); + _poses = currentStateNode->evaluate(animVars, dt, triggersOut); } return _poses; } @@ -92,7 +92,7 @@ void AnimStateMachine::addState(State::Pointer state) { _states.push_back(state); } -void AnimStateMachine::switchState(const AnimVariantMap& animVars, const AnimContext& context, State::Pointer desiredState) { +void AnimStateMachine::switchState(const AnimVariantMap& animVars, State::Pointer desiredState) { const float FRAMES_PER_SECOND = 30.0f; @@ -114,7 +114,7 @@ void AnimStateMachine::switchState(const AnimVariantMap& animVars, const AnimCon _prevPoses = _poses; // snapshot next pose at the target frame. nextStateNode->setCurrentFrame(desiredState->_interpTarget); - _nextPoses = nextStateNode->evaluate(animVars, context, dt, triggers); + _nextPoses = nextStateNode->evaluate(animVars, dt, triggers); } else if (_interpType == InterpType::SnapshotPrev) { // snapshot previoius pose _prevPoses = _poses; diff --git a/libraries/animation/src/AnimStateMachine.h b/libraries/animation/src/AnimStateMachine.h index 711326a9ae..d92b94d1b5 100644 --- a/libraries/animation/src/AnimStateMachine.h +++ b/libraries/animation/src/AnimStateMachine.h @@ -113,7 +113,7 @@ public: explicit AnimStateMachine(const QString& id); virtual ~AnimStateMachine() override; - virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) override; + virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) override; void setCurrentStateVar(QString& currentStateVar) { _currentStateVar = currentStateVar; } @@ -123,7 +123,7 @@ protected: void addState(State::Pointer state); - void switchState(const AnimVariantMap& animVars, const AnimContext& context, State::Pointer desiredState); + void switchState(const AnimVariantMap& animVars, State::Pointer desiredState); State::Pointer evaluateTransitions(const AnimVariantMap& animVars) const; // for AnimDebugDraw rendering diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 3276b6d6ba..45790524d1 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -298,6 +298,7 @@ void Rig::clearJointAnimationPriority(int index) { void Rig::clearIKJointLimitHistory() { if (_animNode) { _animNode->traverse([&](AnimNode::Pointer node) { + // only report clip nodes as valid roles. auto ikNode = std::dynamic_pointer_cast(node); if (ikNode) { ikNode->clearIKJointLimitHistory(); @@ -307,30 +308,6 @@ void Rig::clearIKJointLimitHistory() { } } -void Rig::updateMaxHipsOffsetLength(float maxLength, float deltaTime) { - - _desiredMaxHipsOffsetLength = maxLength; - - // OUTOFBODY_HACK: smoothly update update _hipsOffsetLength, otherwise we risk introducing oscillation in the hips offset. - const float MAX_HIPS_OFFSET_TIMESCALE = 0.33f; - float tau = deltaTime / MAX_HIPS_OFFSET_TIMESCALE; - _maxHipsOffsetLength = (1.0f - tau) * _maxHipsOffsetLength + tau * _desiredMaxHipsOffsetLength; - - if (_animNode) { - _animNode->traverse([&](AnimNode::Pointer node) { - auto ikNode = std::dynamic_pointer_cast(node); - if (ikNode) { - ikNode->setMaxHipsOffsetLength(_maxHipsOffsetLength); - } - return true; - }); - } -} - -float Rig::getMaxHipsOffsetLength() const { - return _maxHipsOffsetLength; -} - int Rig::getJointParentIndex(int childIndex) const { if (_animSkeleton && isIndexValid(childIndex)) { return _animSkeleton->getParentIndex(childIndex); @@ -518,22 +495,15 @@ bool Rig::getRelativeDefaultJointTranslation(int index, glm::vec3& translationOu } // animation reference speeds. -static const std::vector FORWARD_SPEEDS = { 0.4f, 1.3f, 4.5f }; // m/s -static const std::vector BACKWARD_SPEEDS = { 0.6f, 1.05f }; // m/s -static const std::vector LATERAL_SPEEDS = { 0.2f, 0.5f }; // m/s -static const float DEFAULT_AVATAR_EYE_HEIGHT = 1.65f; // movement speeds are for characters of this eye-height. ~170 cm tall. +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, CharacterControllerState ccState) { glm::vec3 front = worldRotation * IDENTITY_FRONT; glm::vec3 workingVelocity = worldVelocity; - // TODO: account for avatar scaling - int eyeJoint = indexOfJoint("LeftEye"); - int toeJoint = indexOfJoint("LeftToeBase"); - const float AVATAR_EYE_HEIGHT = (eyeJoint >= 0 && toeJoint >= 0) ? getAbsoluteDefaultPose(eyeJoint).trans.y - getAbsoluteDefaultPose(toeJoint).trans.y : DEFAULT_AVATAR_EYE_HEIGHT; - const float AVATAR_HEIGHT_RATIO = DEFAULT_AVATAR_EYE_HEIGHT / AVATAR_EYE_HEIGHT; - { glm::vec3 localVel = glm::inverse(worldRotation) * workingVelocity; @@ -553,22 +523,18 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos float moveBackwardAlpha = 0.0f; float moveLateralAlpha = 0.0f; - float averageForwardSpeed = AVATAR_HEIGHT_RATIO * _averageForwardSpeed.getAverage(); - float averageBackwardSpeed = -averageForwardSpeed; - float averageLateralSpeed = AVATAR_HEIGHT_RATIO * fabsf(_averageLateralSpeed.getAverage()); - // calcuate the animation alpha and timeScale values based on current speeds and animation reference speeds. - calcAnimAlpha(averageForwardSpeed, FORWARD_SPEEDS, &moveForwardAlpha); - calcAnimAlpha(averageBackwardSpeed, BACKWARD_SPEEDS, &moveBackwardAlpha); - calcAnimAlpha(averageLateralSpeed, LATERAL_SPEEDS, &moveLateralAlpha); + calcAnimAlpha(_averageForwardSpeed.getAverage(), FORWARD_SPEEDS, &moveForwardAlpha); + calcAnimAlpha(-_averageForwardSpeed.getAverage(), BACKWARD_SPEEDS, &moveBackwardAlpha); + calcAnimAlpha(fabsf(_averageLateralSpeed.getAverage()), LATERAL_SPEEDS, &moveLateralAlpha); - _animVars.set("moveForwardSpeed", averageForwardSpeed); + _animVars.set("moveForwardSpeed", _averageForwardSpeed.getAverage()); _animVars.set("moveForwardAlpha", moveForwardAlpha); - _animVars.set("moveBackwardSpeed", averageBackwardSpeed); + _animVars.set("moveBackwardSpeed", -_averageForwardSpeed.getAverage()); _animVars.set("moveBackwardAlpha", moveBackwardAlpha); - _animVars.set("moveLateralSpeed", averageLateralSpeed); + _animVars.set("moveLateralSpeed", fabsf(_averageLateralSpeed.getAverage())); _animVars.set("moveLateralAlpha", moveLateralAlpha); const float MOVE_ENTER_SPEED_THRESHOLD = 0.2f; // m/sec @@ -626,7 +592,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos } } - const float STATE_CHANGE_HYSTERESIS_TIMER = 1.0f / 60.0f; + const float STATE_CHANGE_HYSTERESIS_TIMER = 0.1f; // Skip hystersis timer for jump transitions. if (_desiredState == RigRole::Takeoff) { @@ -925,11 +891,9 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { updateAnimationStateHandlers(); _animVars.setRigToGeometryTransform(_rigToGeometryTransform); - AnimContext context(_enableDebugDrawIKTargets, getGeometryToRigTransform()); - // evaluate the animation AnimNode::Triggers triggersOut; - _internalPoseSet._relativePoses = _animNode->evaluate(_animVars, context, deltaTime, triggersOut); + _internalPoseSet._relativePoses = _animNode->evaluate(_animVars, deltaTime, triggersOut); if ((int)_internalPoseSet._relativePoses.size() != _animSkeleton->getNumJoints()) { // animations haven't fully loaded yet. _internalPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses(); @@ -1058,6 +1022,20 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { AnimPose hmdPose(glm::vec3(1.0f), params.rigHeadOrientation * yFlip180, params.rigHeadPosition); computeHeadNeckAnimVars(hmdPose, headPos, headRot, neckPos, neckRot); + // debug rendering +#ifdef DEBUG_RENDERING + const glm::vec4 red(1.0f, 0.0f, 0.0f, 1.0f); + const glm::vec4 green(0.0f, 1.0f, 0.0f, 1.0f); + + // transform from bone into avatar space + AnimPose headPose(glm::vec3(1), headRot, headPos); + DebugDraw::getInstance().addMyAvatarMarker("headTarget", headPose.rot, headPose.trans, red); + + // transform from bone into avatar space + AnimPose neckPose(glm::vec3(1), neckRot, neckPos); + DebugDraw::getInstance().addMyAvatarMarker("neckTarget", neckPose.rot, neckPose.trans, green); +#endif + _animVars.set("headPosition", headPos); _animVars.set("headRotation", headRot); _animVars.set("headType", (int)IKTarget::Type::HmdHead); @@ -1130,9 +1108,8 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) { if (params.isLeftEnabled) { - glm::vec3 handPosition = params.leftPosition; - // prevent the hand IK targets from intersecting the body capsule + glm::vec3 handPosition = params.leftPosition; glm::vec3 displacement; if (findSphereCapsulePenetration(handPosition, HAND_RADIUS, bodyCapsuleStart, bodyCapsuleEnd, bodyCapsuleRadius, displacement)) { handPosition -= displacement; @@ -1149,9 +1126,8 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) { if (params.isRightEnabled) { - glm::vec3 handPosition = params.rightPosition; - // prevent the hand IK targets from intersecting the body capsule + glm::vec3 handPosition = params.rightPosition; glm::vec3 displacement; if (findSphereCapsulePenetration(handPosition, HAND_RADIUS, bodyCapsuleStart, bodyCapsuleEnd, bodyCapsuleRadius, displacement)) { handPosition -= displacement; @@ -1403,10 +1379,9 @@ void Rig::computeAvatarBoundingCapsule( // call overlay twice: once to verify AnimPoseVec joints and again to do the IK AnimNode::Triggers triggersOut; - AnimContext context(false, glm::mat4()); float dt = 1.0f; // the value of this does not matter - ikNode.overlay(animVars, context, dt, triggersOut, _animSkeleton->getRelativeBindPoses()); - AnimPoseVec finalPoses = ikNode.overlay(animVars, context, dt, triggersOut, _animSkeleton->getRelativeBindPoses()); + ikNode.overlay(animVars, dt, triggersOut, _animSkeleton->getRelativeBindPoses()); + AnimPoseVec finalPoses = ikNode.overlay(animVars, dt, triggersOut, _animSkeleton->getRelativeBindPoses()); // convert relative poses to absolute _animSkeleton->convertRelativePosesToAbsolute(finalPoses); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 2c43d83876..151a7ae8e9 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -104,8 +104,6 @@ public: void clearJointAnimationPriority(int index); void clearIKJointLimitHistory(); - void updateMaxHipsOffsetLength(float maxLength, float deltaTime); - float getMaxHipsOffsetLength() const; int getJointParentIndex(int childIndex) const; @@ -215,8 +213,6 @@ public: const glm::mat4& getGeometryToRigTransform() const { return _geometryToRigTransform; } - void setEnableDebugDrawIKTargets(bool enableDebugDrawIKTargets) { _enableDebugDrawIKTargets = enableDebugDrawIKTargets; } - signals: void onLoadComplete(); @@ -319,12 +315,6 @@ protected: bool _enableInverseKinematics { true }; mutable uint32_t _jointNameWarningCount { 0 }; - glm::vec3 _desiredRigHeadPosition; - bool _truncateIKTargets { false }; - bool _enableDebugDrawIKTargets { false }; - - float _maxHipsOffsetLength { 1.0f }; - float _desiredMaxHipsOffsetLength { 1.0f }; private: QMap _stateHandlers; diff --git a/libraries/physics/src/BulletUtil.h b/libraries/physics/src/BulletUtil.h index c456ed8af8..b6fac74617 100644 --- a/libraries/physics/src/BulletUtil.h +++ b/libraries/physics/src/BulletUtil.h @@ -1,6 +1,6 @@ // // BulletUtil.h -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2014.11.02 // Copyright 2014 High Fidelity, Inc. diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp old mode 100755 new mode 100644 index e15a1eeaf0..5c85f8fc50 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -1,6 +1,6 @@ // // CharacterControllerInterface.cpp -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2015.10.21 // Copyright 2015 High Fidelity, Inc. @@ -13,8 +13,8 @@ #include +#include "PhysicsCollisionGroups.h" #include "ObjectMotionState.h" -#include "PhysicsHelpers.h" #include "PhysicsLogging.h" const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f); @@ -62,10 +62,15 @@ CharacterController::CharacterMotor::CharacterMotor(const glm::vec3& vel, const } CharacterController::CharacterController() { + _halfHeight = 1.0f; + + _enabled = false; + _floorDistance = MAX_FALL_HEIGHT; _targetVelocity.setValue(0.0f, 0.0f, 0.0f); _followDesiredBodyTransform.setIdentity(); + _followTimeRemaining = 0.0f; _jumpSpeed = JUMP_SPEED; _state = State::Hover; _isPushingUp = false; @@ -73,6 +78,9 @@ CharacterController::CharacterController() { _takeoffToInAirStartTime = 0; _jumpButtonDownStartTime = 0; _jumpButtonDownCount = 0; + _followTime = 0.0f; + _followLinearDisplacement = btVector3(0, 0, 0); + _followAngularDisplacement = btQuaternion::getIdentity(); _hasSupport = false; _pendingFlags = PENDING_FLAG_UPDATE_SHAPE; @@ -99,7 +107,6 @@ bool CharacterController::needsAddition() const { void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { if (_dynamicsWorld != world) { - // remove from old world if (_dynamicsWorld) { if (_rigidBody) { _dynamicsWorld->removeRigidBody(_rigidBody); @@ -108,26 +115,16 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { _dynamicsWorld = nullptr; } if (world && _rigidBody) { - // add to new world _dynamicsWorld = world; _pendingFlags &= ~PENDING_FLAG_JUMP; - _dynamicsWorld->addRigidBody(_rigidBody, _collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR); + // Before adding the RigidBody to the world we must save its oldGravity to the side + // because adding an object to the world will overwrite it with the default gravity. + btVector3 oldGravity = _rigidBody->getGravity(); + _dynamicsWorld->addRigidBody(_rigidBody, BULLET_COLLISION_GROUP_MY_AVATAR, BULLET_COLLISION_MASK_MY_AVATAR); _dynamicsWorld->addAction(this); - // restore gravity settings because adding an object to the world overwrites its gravity setting - _rigidBody->setGravity(_gravity * _currentUp); - btCollisionShape* shape = _rigidBody->getCollisionShape(); - assert(shape && shape->getShapeType() == CONVEX_HULL_SHAPE_PROXYTYPE); - _ghost.setCharacterShape(static_cast(shape)); + // restore gravity settings + _rigidBody->setGravity(oldGravity); } - // KINEMATIC_CONTROLLER_HACK - _ghost.setCollisionGroupAndMask(_collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR & (~ _collisionGroup)); - _ghost.setCollisionWorld(_dynamicsWorld); - _ghost.setRadiusAndHalfHeight(_radius, _halfHeight); - _ghost.setMaxStepHeight(0.75f * (_radius + _halfHeight)); // HACK - _ghost.setMinWallAngle(PI / 4.0f); // HACK - _ghost.setUpDirection(_currentUp); - _ghost.setMotorOnly(!_moveKinematically); - _ghost.setWorldTransform(_rigidBody->getWorldTransform()); } if (_dynamicsWorld) { if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) { @@ -141,84 +138,38 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { } } -bool CharacterController::checkForSupport(btCollisionWorld* collisionWorld, btScalar dt) { - if (_moveKinematically) { - // kinematic motion will move() the _ghost later - return _ghost.hasSupport(); - } - - bool pushing = _targetVelocity.length2() > FLT_EPSILON; - - btDispatcher* dispatcher = collisionWorld->getDispatcher(); - int numManifolds = dispatcher->getNumManifolds(); - bool hasFloor = false; - - btTransform rotation = _rigidBody->getWorldTransform(); - rotation.setOrigin(btVector3(0.0f, 0.0f, 0.0f)); // clear translation part +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++) { - btPersistentManifold* contactManifold = dispatcher->getManifoldByIndexInternal(i); - if (_rigidBody == contactManifold->getBody1() || _rigidBody == contactManifold->getBody0()) { - bool characterIsFirst = _rigidBody == contactManifold->getBody0(); + btPersistentManifold* contactManifold = collisionWorld->getDispatcher()->getManifoldByIndexInternal(i); + const btCollisionObject* obA = static_cast(contactManifold->getBody0()); + const btCollisionObject* obB = static_cast(contactManifold->getBody1()); + if (obA == _rigidBody || obB == _rigidBody) { int numContacts = contactManifold->getNumContacts(); - int stepContactIndex = -1; - float highestStep = _minStepHeight; for (int j = 0; j < numContacts; j++) { - // check for "floor" - btManifoldPoint& contact = contactManifold->getContactPoint(j); - btVector3 pointOnCharacter = characterIsFirst ? contact.m_localPointA : contact.m_localPointB; // object-local-frame - btVector3 normal = characterIsFirst ? contact.m_normalWorldOnB : -contact.m_normalWorldOnB; // points toward character - btScalar hitHeight = _halfHeight + _radius + pointOnCharacter.dot(_currentUp); - if (hitHeight < _maxStepHeight && normal.dot(_currentUp) > _minFloorNormalDotUp) { - hasFloor = true; - if (!pushing) { - // we're not pushing against anything so we can early exit - // (all we need to know is that there is a floor) - break; - } + 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(); + btVector3 normal = (obA == _rigidBody) ? pt.m_normalWorldOnB : -pt.m_normalWorldOnB; + if (contactPointY < -_halfHeight && normal.dot(_currentUp) > COS_PI_OVER_THREE) { + return true; } - if (pushing && _targetVelocity.dot(normal) < 0.0f) { - // remember highest step obstacle - if (!_stepUpEnabled || hitHeight > _maxStepHeight) { - // this manifold is invalidated by point that is too high - stepContactIndex = -1; - break; - } else if (hitHeight > highestStep && normal.dot(_targetVelocity) < 0.0f ) { - highestStep = hitHeight; - stepContactIndex = j; - hasFloor = true; - } - } - } - if (stepContactIndex > -1 && highestStep > _stepHeight) { - // remember step info for later - btManifoldPoint& contact = contactManifold->getContactPoint(stepContactIndex); - btVector3 pointOnCharacter = characterIsFirst ? contact.m_localPointA : contact.m_localPointB; // object-local-frame - _stepNormal = characterIsFirst ? contact.m_normalWorldOnB : -contact.m_normalWorldOnB; // points toward character - _stepHeight = highestStep; - _stepPoint = rotation * pointOnCharacter; // rotate into world-frame - } - if (hasFloor && !(pushing && _stepUpEnabled)) { - // early exit since all we need to know is that we're on a floor - break; } } } - return hasFloor; -} - -void CharacterController::updateAction(btCollisionWorld* collisionWorld, btScalar deltaTime) { - _preActionVelocity = getLinearVelocity(); - preStep(collisionWorld); - playerStep(collisionWorld, deltaTime); + return false; } void CharacterController::preStep(btCollisionWorld* collisionWorld) { // trace a ray straight down to see if we're standing on the ground - const btTransform& transform = _rigidBody->getWorldTransform(); + const btTransform& xform = _rigidBody->getWorldTransform(); // rayStart is at center of bottom sphere - btVector3 rayStart = transform.getOrigin() - _halfHeight * _currentUp; + btVector3 rayStart = xform.getOrigin() - _halfHeight * _currentUp; // rayEnd is some short distance outside bottom sphere const btScalar FLOOR_PROXIMITY_THRESHOLD = 0.3f * _radius; @@ -232,117 +183,54 @@ void CharacterController::preStep(btCollisionWorld* collisionWorld) { if (rayCallback.hasHit()) { _floorDistance = rayLength * rayCallback.m_closestHitFraction - _radius; } + + _hasSupport = checkForSupport(collisionWorld); } const btScalar MIN_TARGET_SPEED = 0.001f; const btScalar MIN_TARGET_SPEED_SQUARED = MIN_TARGET_SPEED * MIN_TARGET_SPEED; -void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar dt) { - _stepHeight = _minStepHeight; // clears memory of last step obstacle +void CharacterController::playerStep(btCollisionWorld* dynaWorld, btScalar dt) { btVector3 velocity = _rigidBody->getLinearVelocity() - _parentVelocity; - if (_following) { - _followTimeAccumulator += dt; - // linear part uses a motor - const float MAX_WALKING_SPEED = 30.5f; // TODO: scale this stuff with avatar size - const float MAX_WALKING_SPEED_DISTANCE = 1.0f; - const float NORMAL_WALKING_SPEED = 0.5f * MAX_WALKING_SPEED; - const float NORMAL_WALKING_SPEED_DISTANCE = 0.5f * MAX_WALKING_SPEED_DISTANCE; - const float FEW_SUBSTEPS = 4.0f * dt; + computeNewVelocity(dt, velocity); + _rigidBody->setLinearVelocity(velocity + _parentVelocity); + + // Dynamicaly compute a follow velocity to move this body toward the _followDesiredBodyTransform. + // Rather than add this velocity to velocity the RigidBody, we explicitly teleport the RigidBody towards its goal. + // This mirrors the computation done in MyAvatar::FollowHelper::postPhysicsUpdate(). + + const float MINIMUM_TIME_REMAINING = 0.005f; + const float MAX_DISPLACEMENT = 0.5f * _radius; + _followTimeRemaining -= dt; + if (_followTimeRemaining >= MINIMUM_TIME_REMAINING) { btTransform bodyTransform = _rigidBody->getWorldTransform(); + btVector3 startPos = bodyTransform.getOrigin(); btVector3 deltaPos = _followDesiredBodyTransform.getOrigin() - startPos; - btScalar deltaDistance = deltaPos.length(); - const float MIN_DELTA_DISTANCE = 0.01f; // TODO: scale by avatar size but cap at (NORMAL_WALKING_SPEED * FEW_SUBSTEPS) - if (deltaDistance > MIN_DELTA_DISTANCE) { - btVector3 vel = deltaPos; - if (_state == State::Hover) { - btScalar HOVER_FOLLOW_VELOCITY_TIMESCALE = 0.1f; - vel /= HOVER_FOLLOW_VELOCITY_TIMESCALE; - } else { - if (deltaDistance > MAX_WALKING_SPEED_DISTANCE) { - // cap max speed - vel *= MAX_WALKING_SPEED / deltaDistance; - } else if (deltaDistance > NORMAL_WALKING_SPEED_DISTANCE) { - // linearly interpolate to NORMAL_WALKING_SPEED - btScalar alpha = (deltaDistance - NORMAL_WALKING_SPEED_DISTANCE) / (MAX_WALKING_SPEED_DISTANCE - NORMAL_WALKING_SPEED_DISTANCE); - vel *= NORMAL_WALKING_SPEED * (1.0f - alpha) + MAX_WALKING_SPEED * alpha; - } else { - // use exponential decay but cap at NORMAL_WALKING_SPEED - vel /= FEW_SUBSTEPS; - btScalar speed = vel.length(); - if (speed > NORMAL_WALKING_SPEED) { - vel *= NORMAL_WALKING_SPEED / speed; - } - } - } - vel += _followVelocity; - const float HORIZONTAL_FOLLOW_TIMESCALE = 0.05f; - float verticalFollowTimescale = 20.0f; - if (_state == State::Hover) { - verticalFollowTimescale = HORIZONTAL_FOLLOW_TIMESCALE; - } else { - // remove vertical component - vel -= vel.dot(_currentUp) * _currentUp; - } - glm::quat worldFrameRotation; // identity - addMotor(bulletToGLM(vel), worldFrameRotation, HORIZONTAL_FOLLOW_TIMESCALE, verticalFollowTimescale); - } + btVector3 vel = deltaPos / _followTimeRemaining; + btVector3 linearDisplacement = clampLength(vel * dt, MAX_DISPLACEMENT); // clamp displacement to prevent tunneling. + btVector3 endPos = startPos + linearDisplacement; - // angular part uses incremental teleports - const float ANGULAR_FOLLOW_TIMESCALE = 0.8f; - const float MAX_ANGULAR_SPEED = (PI / 2.0f) / ANGULAR_FOLLOW_TIMESCALE; btQuaternion startRot = bodyTransform.getRotation(); glm::vec2 currentFacing = getFacingDir2D(bulletToGLM(startRot)); - glm::vec2 currentRight(currentFacing.y, - currentFacing.x); + glm::vec2 currentRight(currentFacing.y, -currentFacing.x); glm::vec2 desiredFacing = getFacingDir2D(bulletToGLM(_followDesiredBodyTransform.getRotation())); float deltaAngle = acosf(glm::clamp(glm::dot(currentFacing, desiredFacing), -1.0f, 1.0f)); - float angularSpeed = deltaAngle / FEW_SUBSTEPS; - if (angularSpeed > MAX_ANGULAR_SPEED) { - angularSpeed *= MAX_ANGULAR_SPEED / angularSpeed; - } + float angularSpeed = deltaAngle / _followTimeRemaining; float sign = copysignf(1.0f, glm::dot(desiredFacing, currentRight)); btQuaternion angularDisplacement = btQuaternion(btVector3(0.0f, 1.0f, 0.0f), sign * angularSpeed * dt); btQuaternion endRot = angularDisplacement * startRot; - _rigidBody->setWorldTransform(btTransform(endRot, startPos)); - } - - _hasSupport = checkForSupport(collisionWorld, dt); - computeNewVelocity(dt, velocity); - - if (_moveKinematically) { - // KINEMATIC_CONTROLLER_HACK - btTransform transform = _rigidBody->getWorldTransform(); - transform.setOrigin(_ghost.getWorldTransform().getOrigin()); - _ghost.setWorldTransform(transform); - _ghost.setMotorVelocity(_targetVelocity); - float overshoot = 1.0f * _radius; - _ghost.move(dt, overshoot, _gravity); - transform.setOrigin(_ghost.getWorldTransform().getOrigin()); - _rigidBody->setWorldTransform(transform); - _rigidBody->setLinearVelocity(_ghost.getLinearVelocity()); - } else { - float stepUpSpeed2 = _stepUpVelocity.length2(); - if (stepUpSpeed2 > FLT_EPSILON) { - // we step up with teleports rather than applying velocity - // use a speed that would ballistically reach _stepHeight under gravity - _stepUpVelocity /= sqrtf(stepUpSpeed2); - btScalar minStepUpSpeed = sqrtf(fabsf(2.0f * _gravity * _stepHeight)); - - btTransform transform = _rigidBody->getWorldTransform(); - transform.setOrigin(transform.getOrigin() + (dt * minStepUpSpeed) * _stepUpVelocity); - _rigidBody->setWorldTransform(transform); - - // make sure the upward velocity is large enough to clear the very top of the step - const btScalar MAGIC_STEP_OVERSHOOT_SPEED_COEFFICIENT = 0.5f; - minStepUpSpeed = MAGIC_STEP_OVERSHOOT_SPEED_COEFFICIENT * sqrtf(fabsf(2.0f * _gravity * _minStepHeight)); - btScalar vDotUp = velocity.dot(_currentUp); - if (vDotUp < minStepUpSpeed) { - velocity += (minStepUpSpeed - vDotUp) * _stepUpVelocity; - } - } - _rigidBody->setLinearVelocity(velocity + _parentVelocity); - _ghost.setWorldTransform(_rigidBody->getWorldTransform()); + + // in order to accumulate displacement of avatar position, we need to take _shapeLocalOffset into account. + btVector3 shapeLocalOffset = glmToBullet(_shapeLocalOffset); + btVector3 swingDisplacement = rotateVector(endRot, -shapeLocalOffset) - rotateVector(startRot, -shapeLocalOffset); + + _followLinearDisplacement = linearDisplacement + swingDisplacement + _followLinearDisplacement; + _followAngularDisplacement = angularDisplacement * _followAngularDisplacement; + + _rigidBody->setWorldTransform(btTransform(endRot, endPos)); } + _followTime += dt; } void CharacterController::jump() { @@ -384,96 +272,95 @@ void CharacterController::setState(State desiredState) { #ifdef DEBUG_STATE_CHANGE qCDebug(physics) << "CharacterController::setState" << stateToStr(desiredState) << "from" << stateToStr(_state) << "," << reason; #endif - if (_rigidBody) { - if (desiredState == State::Hover && _state != State::Hover) { - // hover enter + 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 (_collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS) { - _rigidBody->setGravity(btVector3(0.0f, 0.0f, 0.0f)); - } else { - _rigidBody->setGravity(_gravity * _currentUp); - } + } + } 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& minCorner, const glm::vec3& scale) { - float x = scale.x; - float z = scale.z; +void CharacterController::setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale) { + _boxScale = scale; + + float x = _boxScale.x; + float z = _boxScale.z; float radius = 0.5f * sqrtf(0.5f * (x * x + z * z)); - float halfHeight = 0.5f * scale.y - radius; + float halfHeight = 0.5f * _boxScale.y - radius; float MIN_HALF_HEIGHT = 0.1f; if (halfHeight < MIN_HALF_HEIGHT) { halfHeight = MIN_HALF_HEIGHT; } // compare dimensions - if (glm::abs(radius - _radius) > FLT_EPSILON || glm::abs(halfHeight - _halfHeight) > FLT_EPSILON) { - _radius = radius; - _halfHeight = halfHeight; - const btScalar DEFAULT_MIN_STEP_HEIGHT = 0.041f; // HACK: hardcoded now but should just larger than shape margin - const btScalar MAX_STEP_FRACTION_OF_HALF_HEIGHT = 0.56f; - _minStepHeight = DEFAULT_MIN_STEP_HEIGHT; - _maxStepHeight = MAX_STEP_FRACTION_OF_HALF_HEIGHT * (_halfHeight + _radius); - + float radiusDelta = glm::abs(radius - _radius); + float heightDelta = glm::abs(halfHeight - _halfHeight); + if (radiusDelta < FLT_EPSILON && heightDelta < FLT_EPSILON) { + // shape hasn't changed --> nothing to do + } else { if (_dynamicsWorld) { // must REMOVE from world prior to shape update _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION; } _pendingFlags |= PENDING_FLAG_UPDATE_SHAPE; - _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION; + // only need to ADD back when we happen to be enabled + if (_enabled) { + _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION; + } } // it's ok to change offset immediately -- there are no thread safety issues here - _shapeLocalOffset = minCorner + 0.5f * scale; + _shapeLocalOffset = corner + 0.5f * _boxScale; } -void CharacterController::setCollisionGroup(int16_t group) { - if (_collisionGroup != group) { - _collisionGroup = group; - _pendingFlags |= PENDING_FLAG_UPDATE_COLLISION_GROUP; - _ghost.setCollisionGroupAndMask(_collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR & (~ _collisionGroup)); - } -} - -void CharacterController::handleChangedCollisionGroup() { - if (_pendingFlags & PENDING_FLAG_UPDATE_COLLISION_GROUP) { - // ATM the easiest way to update collision groups is to remove/re-add the RigidBody - if (_dynamicsWorld) { - _dynamicsWorld->removeRigidBody(_rigidBody); - _dynamicsWorld->addRigidBody(_rigidBody, _collisionGroup, BULLET_COLLISION_MASK_MY_AVATAR); - } - _pendingFlags &= ~PENDING_FLAG_UPDATE_COLLISION_GROUP; - - if (_state != State::Hover && _rigidBody) { - _gravity = _collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS ? 0.0f : DEFAULT_CHARACTER_GRAVITY; - _rigidBody->setGravity(_gravity * _currentUp); +void CharacterController::setEnabled(bool enabled) { + if (enabled != _enabled) { + if (enabled) { + // Don't bother clearing REMOVE bit since it might be paired with an UPDATE_SHAPE bit. + // Setting the ADD bit here works for all cases so we don't even bother checking other bits. + _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION; + } else { + if (_dynamicsWorld) { + _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION; + } + _pendingFlags &= ~ PENDING_FLAG_ADD_TO_SIMULATION; } + SET_STATE(State::Hover, "setEnabled"); + _enabled = enabled; } } void CharacterController::updateUpAxis(const glm::quat& rotation) { + btVector3 oldUp = _currentUp; _currentUp = quatRotate(glmToBullet(rotation), LOCAL_UP_AXIS); - _ghost.setUpDirection(_currentUp); - if (_state != State::Hover && _rigidBody) { - _rigidBody->setGravity(_gravity * _currentUp); + if (_state != State::Hover) { + const btScalar MIN_UP_ERROR = 0.01f; + if (oldUp.distance(_currentUp) > MIN_UP_ERROR) { + _rigidBody->setGravity(DEFAULT_CHARACTER_GRAVITY * _currentUp); + } } } void CharacterController::setPositionAndOrientation( const glm::vec3& position, const glm::quat& orientation) { + // TODO: update gravity if up has changed updateUpAxis(orientation); - _rotation = glmToBullet(orientation); - _position = glmToBullet(position + orientation * _shapeLocalOffset); + + btQuaternion bodyOrientation = glmToBullet(orientation); + btVector3 bodyPosition = glmToBullet(position + orientation * _shapeLocalOffset); + _characterBodyTransform = btTransform(bodyOrientation, bodyPosition); } void CharacterController::getPositionAndOrientation(glm::vec3& position, glm::quat& rotation) const { - if (_rigidBody) { + if (_enabled && _rigidBody) { const btTransform& avatarTransform = _rigidBody->getWorldTransform(); rotation = bulletToGLM(avatarTransform.getRotation()); position = bulletToGLM(avatarTransform.getOrigin()) - rotation * _shapeLocalOffset; @@ -484,43 +371,25 @@ void CharacterController::setParentVelocity(const glm::vec3& velocity) { _parentVelocity = glmToBullet(velocity); } -void CharacterController::setFollowParameters(const glm::mat4& desiredWorldBodyMatrix) { +void CharacterController::setFollowParameters(const glm::mat4& desiredWorldBodyMatrix, float timeRemaining) { + _followTimeRemaining = timeRemaining; _followDesiredBodyTransform = glmToBullet(desiredWorldBodyMatrix) * btTransform(btQuaternion::getIdentity(), glmToBullet(_shapeLocalOffset)); - if (!_following) { - _following = true; - _followVelocity = btVector3(0.0f, 0.0f, 0.0f); - } else if (_followTimeAccumulator > 0.0f) { - btVector3 newFollowVelocity = (_followDesiredBodyTransform.getOrigin() - _previousFollowPosition) / _followTimeAccumulator; - const float dontDivideByZero = 0.001f; - float newSpeed = newFollowVelocity.length() + dontDivideByZero; - float oldSpeed = _followVelocity.length(); +} - bool successfulSnap = false; - btVector3 offset = _followDesiredBodyTransform.getOrigin() - _position; - const btScalar MAX_FOLLOW_OFFSET = 10.0f * _radius; - if (offset.length() > MAX_FOLLOW_OFFSET) { - successfulSnap = queryPenetration(_followDesiredBodyTransform); - if (successfulSnap) { - _position = _followDesiredBodyTransform.getOrigin(); - } - } +glm::vec3 CharacterController::getFollowLinearDisplacement() const { + return bulletToGLM(_followLinearDisplacement); +} - const float VERY_SLOW_HOVER_SPEED = 0.25f; - const float FAST_CHANGE_SPEED_RATIO = 100.0f; - if (successfulSnap || (oldSpeed / newSpeed > FAST_CHANGE_SPEED_RATIO && newSpeed < VERY_SLOW_HOVER_SPEED)) { - // character is snapping to avatar position or avatar is stopping quickly - // HACK: slam _followVelocity and _rigidBody velocity immediately - _followVelocity = newFollowVelocity; - newFollowVelocity.setY(0.0f); - _rigidBody->setLinearVelocity(newFollowVelocity); - } else { - // use simple blending to filter noise from the velocity measurement - const float blend = 0.2f; - _followVelocity = (1.0f - blend) * _followVelocity + blend * newFollowVelocity; - } +glm::quat CharacterController::getFollowAngularDisplacement() const { + return bulletToGLM(_followAngularDisplacement); +} + +glm::vec3 CharacterController::getFollowVelocity() const { + if (_followTime > 0.0f) { + return bulletToGLM(_followLinearDisplacement) / _followTime; + } else { + return glm::vec3(); } - _previousFollowPosition = _followDesiredBodyTransform.getOrigin(); - _followTimeAccumulator = 0.0f; } glm::vec3 CharacterController::getLinearVelocity() const { @@ -531,10 +400,6 @@ glm::vec3 CharacterController::getLinearVelocity() const { return velocity; } -glm::vec3 CharacterController::getPreActionLinearVelocity() const { - return _preActionVelocity; -} - glm::vec3 CharacterController::getVelocityChange() const { if (_rigidBody) { return bulletToGLM(_velocityChange); @@ -563,18 +428,16 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel btScalar angle = motor.rotation.getAngle(); btVector3 velocity = worldVelocity.rotate(axis, -angle); - if (_collisionGroup == BULLET_COLLISION_GROUP_COLLISIONLESS || - _state == State::Hover || motor.hTimescale == motor.vTimescale) { + if (_state == State::Hover || motor.hTimescale == motor.vTimescale) { // modify velocity btScalar tau = dt / motor.hTimescale; if (tau > 1.0f) { tau = 1.0f; } - velocity += tau * (motor.velocity - velocity); + velocity += (motor.velocity - velocity) * tau; // rotate back into world-frame velocity = velocity.rotate(axis, angle); - _targetVelocity += (tau * motor.velocity).rotate(axis, angle); // store the velocity and weight velocities.push_back(velocity); @@ -582,32 +445,12 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel } else { // compute local UP btVector3 up = _currentUp.rotate(axis, -angle); - btVector3 motorVelocity = motor.velocity; - - // save these non-adjusted components for later - btVector3 vTargetVelocity = motorVelocity.dot(up) * up; - btVector3 hTargetVelocity = motorVelocity - vTargetVelocity; - - if (_stepHeight > _minStepHeight) { - // there is a step --> compute velocity direction to go over step - btVector3 motorVelocityWF = motorVelocity.rotate(axis, angle); - if (motorVelocityWF.dot(_stepNormal) < 0.0f) { - // the motor pushes against step - motorVelocityWF = _stepNormal.cross(_stepPoint.cross(motorVelocityWF)); - btScalar doubleCrossLength2 = motorVelocityWF.length2(); - if (doubleCrossLength2 > FLT_EPSILON) { - // scale the motor in the correct direction and rotate back to motor-frame - motorVelocityWF *= (motorVelocity.length() / sqrtf(doubleCrossLength2)); - _stepUpVelocity += motorVelocityWF.rotate(axis, -angle); - } - } - } // split velocity into horizontal and vertical components btVector3 vVelocity = velocity.dot(up) * up; btVector3 hVelocity = velocity - vVelocity; - btVector3 vMotorVelocity = motorVelocity.dot(up) * up; - btVector3 hMotorVelocity = motorVelocity - vMotorVelocity; + btVector3 vTargetVelocity = motor.velocity.dot(up) * up; + btVector3 hTargetVelocity = motor.velocity - vTargetVelocity; // modify each component separately btScalar maxTau = 0.0f; @@ -617,7 +460,7 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel tau = 1.0f; } maxTau = tau; - hVelocity += (hMotorVelocity - hVelocity) * tau; + hVelocity += (hTargetVelocity - hVelocity) * tau; } if (motor.vTimescale < MAX_CHARACTER_MOTOR_TIMESCALE) { btScalar tau = dt / motor.vTimescale; @@ -627,12 +470,11 @@ void CharacterController::applyMotor(int index, btScalar dt, btVector3& worldVel if (tau > maxTau) { maxTau = tau; } - vVelocity += (vMotorVelocity - vVelocity) * tau; + vVelocity += (vTargetVelocity - vVelocity) * tau; } // add components back together and rotate into world-frame velocity = (hVelocity + vVelocity).rotate(axis, angle); - _targetVelocity += maxTau * (hTargetVelocity + vTargetVelocity).rotate(axis, angle); // store velocity and weights velocities.push_back(velocity); @@ -650,8 +492,6 @@ void CharacterController::computeNewVelocity(btScalar dt, btVector3& velocity) { velocities.reserve(_motors.size()); std::vector weights; weights.reserve(_motors.size()); - _targetVelocity = btVector3(0.0f, 0.0f, 0.0f); - _stepUpVelocity = btVector3(0.0f, 0.0f, 0.0f); for (int i = 0; i < (int)_motors.size(); ++i) { applyMotor(i, dt, velocity, velocities, weights); } @@ -667,18 +507,14 @@ void CharacterController::computeNewVelocity(btScalar dt, btVector3& velocity) { for (size_t i = 0; i < velocities.size(); ++i) { velocity += (weights[i] / totalWeight) * velocities[i]; } - _targetVelocity /= totalWeight; } if (velocity.length2() < MIN_TARGET_SPEED_SQUARED) { velocity = btVector3(0.0f, 0.0f, 0.0f); } // 'thrust' is applied at the very end - _targetVelocity += dt * _linearAcceleration; velocity += dt * _linearAcceleration; - // Note the differences between these two variables: - // _targetVelocity = ideal final velocity according to input - // velocity = real final velocity after motors are applied to current velocity + _targetVelocity = velocity; } void CharacterController::computeNewVelocity(btScalar dt, glm::vec3& velocity) { @@ -687,54 +523,57 @@ void CharacterController::computeNewVelocity(btScalar dt, glm::vec3& velocity) { velocity = bulletToGLM(btVelocity); } -void CharacterController::updateState() { - if (!_dynamicsWorld) { - return; - } - const btScalar FLY_TO_GROUND_THRESHOLD = 0.1f * _radius; - const btScalar GROUND_TO_FLY_THRESHOLD = 0.8f * _radius + _halfHeight; - const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 250 * MSECS_PER_SECOND; - const btScalar MIN_HOVER_HEIGHT = 2.5f; - const quint64 JUMP_TO_HOVER_PERIOD = 1100 * MSECS_PER_SECOND; +void CharacterController::preSimulation() { + if (_enabled && _dynamicsWorld && _rigidBody) { + quint64 now = usecTimestampNow(); - // scan for distant floor - // rayStart is at center of bottom sphere - btVector3 rayStart = _position; + // slam body to where it is supposed to be + _rigidBody->setWorldTransform(_characterBodyTransform); + btVector3 velocity = _rigidBody->getLinearVelocity(); + _preSimulationVelocity = velocity; - // rayEnd is straight down MAX_FALL_HEIGHT - btScalar rayLength = _radius + MAX_FALL_HEIGHT; - btVector3 rayEnd = rayStart - rayLength * _currentUp; + // scan for distant floor + // rayStart is at center of bottom sphere + btVector3 rayStart = _characterBodyTransform.getOrigin(); - ClosestNotMe rayCallback(_rigidBody); - rayCallback.m_closestHitFraction = 1.0f; - _dynamicsWorld->rayTest(rayStart, rayEnd, rayCallback); - bool rayHasHit = rayCallback.hasHit(); - quint64 now = usecTimestampNow(); - if (rayHasHit) { - _rayHitStartTime = now; - _floorDistance = rayLength * rayCallback.m_closestHitFraction - (_radius + _halfHeight); - } else { + // rayEnd is straight down MAX_FALL_HEIGHT + btScalar rayLength = _radius + MAX_FALL_HEIGHT; + btVector3 rayEnd = rayStart - rayLength * _currentUp; + + const btScalar FLY_TO_GROUND_THRESHOLD = 0.1f * _radius; + const btScalar GROUND_TO_FLY_THRESHOLD = 0.8f * _radius + _halfHeight; + const quint64 TAKE_OFF_TO_IN_AIR_PERIOD = 250 * MSECS_PER_SECOND; + const btScalar MIN_HOVER_HEIGHT = 2.5f; + const quint64 JUMP_TO_HOVER_PERIOD = 1100 * MSECS_PER_SECOND; + const btScalar MAX_WALKING_SPEED = 2.5f; const quint64 RAY_HIT_START_PERIOD = 500 * MSECS_PER_SECOND; - if ((now - _rayHitStartTime) < RAY_HIT_START_PERIOD) { + + ClosestNotMe rayCallback(_rigidBody); + rayCallback.m_closestHitFraction = 1.0f; + _dynamicsWorld->rayTest(rayStart, rayEnd, rayCallback); + bool rayHasHit = rayCallback.hasHit(); + if (rayHasHit) { + _rayHitStartTime = now; + _floorDistance = rayLength * rayCallback.m_closestHitFraction - (_radius + _halfHeight); + } else if ((now - _rayHitStartTime) < RAY_HIT_START_PERIOD) { rayHasHit = true; } else { _floorDistance = FLT_MAX; } - } - // record a time stamp when the jump button was first pressed. - bool jumpButtonHeld = _pendingFlags & PENDING_FLAG_JUMP; - if ((_previousFlags & PENDING_FLAG_JUMP) != (_pendingFlags & PENDING_FLAG_JUMP)) { - if (_pendingFlags & PENDING_FLAG_JUMP) { - _jumpButtonDownStartTime = now; - _jumpButtonDownCount++; + // 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) { + _jumpButtonDownStartTime = now; + _jumpButtonDownCount++; + } } - } - btVector3 velocity = _preSimulationVelocity; + bool jumpButtonHeld = _pendingFlags & PENDING_FLAG_JUMP; + + btVector3 actualHorizVelocity = velocity - velocity.dot(_currentUp) * _currentUp; + bool flyingFast = _state == State::Hover && actualHorizVelocity.length() > (MAX_WALKING_SPEED * 0.75f); - // OUTOFBODY_HACK -- disable normal state transitions while collisionless - if (_collisionGroup == BULLET_COLLISION_GROUP_MY_AVATAR) { switch (_state) { case State::Ground: if (!rayHasHit && !_hasSupport) { @@ -774,50 +613,32 @@ void CharacterController::updateState() { break; } case State::Hover: - btVector3 actualHorizVelocity = velocity - velocity.dot(_currentUp) * _currentUp; - const btScalar MAX_WALKING_SPEED = 2.5f; - bool flyingFast = _state == State::Hover && actualHorizVelocity.length() > (MAX_WALKING_SPEED * 0.75f); - - if ((_floorDistance < MIN_HOVER_HEIGHT) && - !(jumpButtonHeld || flyingFast || (now - _jumpButtonDownStartTime) > JUMP_TO_HOVER_PERIOD)) { + if ((_floorDistance < MIN_HOVER_HEIGHT) && !jumpButtonHeld && !flyingFast) { SET_STATE(State::InAir, "near ground"); } else if (((_floorDistance < FLY_TO_GROUND_THRESHOLD) || _hasSupport) && !flyingFast) { SET_STATE(State::Ground, "touching ground"); } break; } - if (_moveKinematically && _ghost.isHovering()) { - SET_STATE(State::Hover, "kinematic motion"); // HACK - } - } else { - // OUTOFBODY_HACK -- in collisionless state switch only between Ground and Hover states - if (rayHasHit) { - SET_STATE(State::Ground, "collisionless above ground"); - } else { - SET_STATE(State::Hover, "collisionless in air"); - } - } -} - -void CharacterController::preSimulation() { - if (_rigidBody) { - // slam body transform and remember velocity - _rigidBody->setWorldTransform(btTransform(btTransform(_rotation, _position))); - _preSimulationVelocity = _rigidBody->getLinearVelocity(); - - updateState(); } _previousFlags = _pendingFlags; _pendingFlags &= ~PENDING_FLAG_JUMP; + + _followTime = 0.0f; + _followLinearDisplacement = btVector3(0, 0, 0); + _followAngularDisplacement = btQuaternion::getIdentity(); } void CharacterController::postSimulation() { - if (_rigidBody) { - _velocityChange = _rigidBody->getLinearVelocity() - _preSimulationVelocity; + // postSimulation() exists for symmetry and just in case we need to do something here later + if (_enabled && _dynamicsWorld && _rigidBody) { + btVector3 velocity = _rigidBody->getLinearVelocity(); + _velocityChange = velocity - _preSimulationVelocity; } } + bool CharacterController::getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation) { if (!_rigidBody) { return false; @@ -834,58 +655,7 @@ void CharacterController::setFlyingAllowed(bool value) { _flyingAllowed = value; if (!_flyingAllowed && _state == State::Hover) { - // OUTOFBODY_HACK -- disable normal state transitions while collisionless - if (_collisionGroup == BULLET_COLLISION_GROUP_MY_AVATAR) { - SET_STATE(State::InAir, "flying not allowed"); - } + SET_STATE(State::InAir, "flying not allowed"); } } } - -float CharacterController::measureMaxHipsOffsetRadius(const glm::vec3& currentHipsOffset, float maxSweepDistance) { - btVector3 hipsOffset = glmToBullet(currentHipsOffset); // rig-frame - btScalar hipsOffsetLength = hipsOffset.length(); - if (hipsOffsetLength > FLT_EPSILON) { - const btTransform& transform = _rigidBody->getWorldTransform(); - - // rotate into world-frame - btTransform rotation = transform; - rotation.setOrigin(btVector3(0.0f, 0.0f, 0.0f)); - btVector3 startPos = transform.getOrigin() - rotation * glmToBullet(_shapeLocalOffset); - btTransform startTransform = transform; - startTransform.setOrigin(startPos); - btVector3 endPos = startPos + rotation * ((maxSweepDistance / hipsOffsetLength) * hipsOffset); - - // sweep test a sphere - btSphereShape sphere(_radius); - CharacterSweepResult result(&_ghost); - btTransform endTransform = startTransform; - endTransform.setOrigin(endPos); - _ghost.sweepTest(&sphere, startTransform, endTransform, result); - - // measure sweep success - if (result.hasHit()) { - maxSweepDistance *= result.m_closestHitFraction; - } - } - return maxSweepDistance; -} - -void CharacterController::setMoveKinematically(bool kinematic) { - if (kinematic != _moveKinematically) { - _moveKinematically = kinematic; - _pendingFlags |= PENDING_FLAG_UPDATE_SHAPE; - _ghost.setMotorOnly(!_moveKinematically); - } -} - -bool CharacterController::queryPenetration(const btTransform& transform) { - btVector3 minBox; - btVector3 maxBox; - _ghost.setWorldTransform(transform); - _ghost.measurePenetration(minBox, maxBox); - btVector3 penetration = minBox; - penetration.setMax(maxBox.absolute()); - const btScalar MIN_PENETRATION_SQUARED = 0.0016f; // 0.04^2 - return penetration.length2() < MIN_PENETRATION_SQUARED; -} diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 75cbc0730b..586ea175e6 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -1,6 +1,6 @@ // // CharacterControllerInterface.h -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2015.10.21 // Copyright 2015 High Fidelity, Inc. @@ -9,8 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_CharacterController_h -#define hifi_CharacterController_h +#ifndef hifi_CharacterControllerInterface_h +#define hifi_CharacterControllerInterface_h #include #include @@ -19,18 +19,12 @@ #include #include -#include -#include - #include "BulletUtil.h" -#include "CharacterGhostObject.h" const uint32_t PENDING_FLAG_ADD_TO_SIMULATION = 1U << 0; const uint32_t PENDING_FLAG_REMOVE_FROM_SIMULATION = 1U << 1; const uint32_t PENDING_FLAG_UPDATE_SHAPE = 1U << 2; const uint32_t PENDING_FLAG_JUMP = 1U << 3; -const uint32_t PENDING_FLAG_UPDATE_COLLISION_GROUP = 1U << 4; -const float DEFAULT_MIN_FLOOR_NORMAL_DOT_UP = cosf(PI / 3.0f); const float DEFAULT_CHARACTER_GRAVITY = -5.0f; @@ -50,7 +44,7 @@ public: bool needsRemoval() const; bool needsAddition() const; - virtual void setDynamicsWorld(btDynamicsWorld* world); + void setDynamicsWorld(btDynamicsWorld* world); btCollisionObject* getCollisionObject() { return _rigidBody; } virtual void updateShapeIfNecessary() = 0; @@ -62,7 +56,10 @@ public: virtual void warp(const btVector3& origin) override { } virtual void debugDraw(btIDebugDraw* debugDrawer) override { } virtual void setUpInterpolate(bool value) override { } - virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTime) override; + virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTime) override { + preStep(collisionWorld); + playerStep(collisionWorld, deltaTime); + } virtual void preStep(btCollisionWorld *collisionWorld) override; virtual void playerStep(btCollisionWorld *collisionWorld, btScalar dt) override; virtual bool canJump() const override { assert(false); return false; } // never call this @@ -72,7 +69,6 @@ public: void clearMotors(); void addMotor(const glm::vec3& velocity, const glm::quat& rotation, float horizTimescale, float vertTimescale = -1.0f); void applyMotor(int index, btScalar dt, btVector3& worldVelocity, std::vector& velocities, std::vector& weights); - void setStepUpEnabled(bool enabled) { _stepUpEnabled = enabled; } void computeNewVelocity(btScalar dt, btVector3& velocity); void computeNewVelocity(btScalar dt, glm::vec3& velocity); @@ -86,11 +82,13 @@ public: void getPositionAndOrientation(glm::vec3& position, glm::quat& rotation) const; void setParentVelocity(const glm::vec3& parentVelocity); - void setFollowParameters(const glm::mat4& desiredWorldBodyMatrix); - void disableFollow() { _following = false; } + void setFollowParameters(const glm::mat4& desiredWorldMatrix, float timeRemaining); + float getFollowTime() const { return _followTime; } + glm::vec3 getFollowLinearDisplacement() const; + glm::quat getFollowAngularDisplacement() const; + glm::vec3 getFollowVelocity() const; glm::vec3 getLinearVelocity() const; - glm::vec3 getPreActionLinearVelocity() const; glm::vec3 getVelocityChange() const; float getCapsuleRadius() const { return _radius; } @@ -105,24 +103,17 @@ public: }; State getState() const { return _state; } - void updateState(); - void setLocalBoundingBox(const glm::vec3& minCorner, const glm::vec3& scale); + void setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale); - bool isEnabledAndReady() const { return _dynamicsWorld; } - - void setCollisionGroup(int16_t group); - int16_t getCollisionGroup() const { return _collisionGroup; } - void handleChangedCollisionGroup(); + bool isEnabled() const { return _enabled; } // thread-safe + void setEnabled(bool enabled); + bool isEnabledAndReady() const { return _enabled && _dynamicsWorld; } bool getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation); void setFlyingAllowed(bool value); - float measureMaxHipsOffsetRadius(const glm::vec3& currentHipsOffset, float maxSweepDistance); - void setMoveKinematically(bool kinematic); // KINEMATIC_CONTROLLER_HACK - - bool queryPenetration(const btTransform& transform); protected: #ifdef DEBUG_STATE_CHANGE @@ -132,7 +123,7 @@ protected: #endif void updateUpAxis(const glm::quat& rotation); - bool checkForSupport(btCollisionWorld* collisionWorld, btScalar dt); + bool checkForSupport(btCollisionWorld* collisionWorld) const; protected: struct CharacterMotor { @@ -145,18 +136,14 @@ protected: }; std::vector _motors; - CharacterGhostObject _ghost; // KINEMATIC_CONTROLLER_HACK btVector3 _currentUp; btVector3 _targetVelocity; btVector3 _parentVelocity; btVector3 _preSimulationVelocity; - glm::vec3 _preActionVelocity; btVector3 _velocityChange; btTransform _followDesiredBodyTransform; - btVector3 _followVelocity { 0.0f, 0.0f, 0.0f }; - btVector3 _previousFollowPosition { 0.0f, 0.0f, 0.0f }; - btVector3 _position; - btQuaternion _rotation; + btScalar _followTimeRemaining; + btTransform _characterBodyTransform; glm::vec3 _shapeLocalOffset; @@ -168,29 +155,21 @@ protected: quint32 _jumpButtonDownCount; quint32 _takeoffJumpButtonID; - // data for walking up steps - btVector3 _stepPoint { 0.0f, 0.0f, 0.0f }; - btVector3 _stepNormal { 0.0f, 0.0f, 0.0f }; - btVector3 _stepUpVelocity { 0.0f, 0.0f, 0.0f }; - btScalar _stepHeight { 0.0f }; - btScalar _minStepHeight { 0.0f }; - btScalar _maxStepHeight { 0.0f }; - btScalar _minFloorNormalDotUp { DEFAULT_MIN_FLOOR_NORMAL_DOT_UP }; - - btScalar _halfHeight { 0.0f }; - btScalar _radius { 0.0f }; + btScalar _halfHeight; + btScalar _radius; btScalar _floorDistance; - bool _stepUpEnabled { true }; bool _hasSupport; - btScalar _gravity { DEFAULT_CHARACTER_GRAVITY }; - btScalar _followTimeAccumulator { 0.0f }; + btScalar _gravity; btScalar _jumpSpeed; + btScalar _followTime; + btVector3 _followLinearDisplacement; + btQuaternion _followAngularDisplacement; btVector3 _linearAcceleration; - bool _following { false }; + std::atomic_bool _enabled; State _state; bool _isPushingUp; @@ -200,8 +179,6 @@ protected: uint32_t _previousFlags { 0 }; bool _flyingAllowed { true }; - bool _moveKinematically { false }; // KINEMATIC_CONTROLLER_HACK - int16_t _collisionGroup { BULLET_COLLISION_GROUP_MY_AVATAR }; }; -#endif // hifi_CharacterController_h +#endif // hifi_CharacterControllerInterface_h diff --git a/libraries/physics/src/CharacterGhostObject.cpp b/libraries/physics/src/CharacterGhostObject.cpp deleted file mode 100755 index 563605cd16..0000000000 --- a/libraries/physics/src/CharacterGhostObject.cpp +++ /dev/null @@ -1,415 +0,0 @@ -// -// CharacterGhostObject.cpp -// libraries/physics/src -// -// Created by Andrew Meadows 2016.08.26 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "CharacterGhostObject.h" - -#include -#include - -#include - -#include "CharacterRayResult.h" -#include "CharacterGhostShape.h" - - -CharacterGhostObject::~CharacterGhostObject() { - removeFromWorld(); - if (_ghostShape) { - delete _ghostShape; - _ghostShape = nullptr; - setCollisionShape(nullptr); - } -} - -void CharacterGhostObject::setCollisionGroupAndMask(int16_t group, int16_t mask) { - _collisionFilterGroup = group; - _collisionFilterMask = mask; - // TODO: if this probe is in the world reset ghostObject overlap cache -} - -void CharacterGhostObject::getCollisionGroupAndMask(int16_t& group, int16_t& mask) const { - group = _collisionFilterGroup; - mask = _collisionFilterMask; -} - -void CharacterGhostObject::setRadiusAndHalfHeight(btScalar radius, btScalar halfHeight) { - _radius = radius; - _halfHeight = halfHeight; -} - -void CharacterGhostObject::setUpDirection(const btVector3& up) { - btScalar length = up.length(); - if (length > FLT_EPSILON) { - _upDirection /= length; - } else { - _upDirection = btVector3(0.0f, 1.0f, 0.0f); - } -} - -void CharacterGhostObject::setMotorVelocity(const btVector3& velocity) { - _motorVelocity = velocity; - if (_motorOnly) { - _linearVelocity = _motorVelocity; - } -} - -// override of btCollisionObject::setCollisionShape() -void CharacterGhostObject::setCharacterShape(btConvexHullShape* shape) { - assert(shape); - // we create our own shape with an expanded Aabb for more reliable sweep tests - if (_ghostShape) { - delete _ghostShape; - } - - _ghostShape = new CharacterGhostShape(static_cast(shape)); - setCollisionShape(_ghostShape); -} - -void CharacterGhostObject::setCollisionWorld(btCollisionWorld* world) { - if (world != _world) { - removeFromWorld(); - _world = world; - addToWorld(); - } -} - -void CharacterGhostObject::move(btScalar dt, btScalar overshoot, btScalar gravity) { - bool oldOnFloor = _onFloor; - _onFloor = false; - _steppingUp = false; - assert(_world && _inWorld); - updateVelocity(dt, gravity); - - // resolve any penetrations before sweeping - int32_t MAX_LOOPS = 4; - int32_t numExtractions = 0; - btVector3 totalPosition(0.0f, 0.0f, 0.0f); - while (numExtractions < MAX_LOOPS) { - if (resolvePenetration(numExtractions)) { - numExtractions = 0; - break; - } - totalPosition += getWorldTransform().getOrigin(); - ++numExtractions; - } - if (numExtractions > 1) { - // penetration resolution was probably oscillating between opposing objects - // so we use the average of the solutions - totalPosition /= btScalar(numExtractions); - btTransform transform = getWorldTransform(); - transform.setOrigin(totalPosition); - setWorldTransform(transform); - - // TODO: figure out how to untrap character - } - btTransform startTransform = getWorldTransform(); - btVector3 startPosition = startTransform.getOrigin(); - if (_onFloor) { - // resolvePenetration() pushed the avatar out of a floor so - // we must updateTraction() before using _linearVelocity - updateTraction(startPosition); - } - - btScalar speed = _linearVelocity.length(); - btVector3 forwardSweep = dt * _linearVelocity; - btScalar stepDistance = dt * speed; - btScalar MIN_SWEEP_DISTANCE = 0.0001f; - if (stepDistance < MIN_SWEEP_DISTANCE) { - // not moving, no need to sweep - updateTraction(startPosition); - return; - } - - // augment forwardSweep to help slow moving sweeps get over steppable ledges - const btScalar MIN_OVERSHOOT = 0.04f; // default margin - if (overshoot < MIN_OVERSHOOT) { - overshoot = MIN_OVERSHOOT; - } - btScalar longSweepDistance = stepDistance + overshoot; - forwardSweep *= longSweepDistance / stepDistance; - - // step forward - CharacterSweepResult result(this); - btTransform nextTransform = startTransform; - nextTransform.setOrigin(startPosition + forwardSweep); - sweepTest(_characterShape, startTransform, nextTransform, result); // forward - - if (!result.hasHit()) { - nextTransform.setOrigin(startPosition + (stepDistance / longSweepDistance) * forwardSweep); - setWorldTransform(nextTransform); - updateTraction(nextTransform.getOrigin()); - return; - } - bool verticalOnly = btFabs(btFabs(_linearVelocity.dot(_upDirection)) - speed) < MIN_OVERSHOOT; - if (verticalOnly) { - // no need to step - nextTransform.setOrigin(startPosition + (result.m_closestHitFraction * stepDistance / longSweepDistance) * forwardSweep); - setWorldTransform(nextTransform); - - if (result.m_hitNormalWorld.dot(_upDirection) > _maxWallNormalUpComponent) { - _floorNormal = result.m_hitNormalWorld; - _floorContact = result.m_hitPointWorld; - _steppingUp = false; - _onFloor = true; - _hovering = false; - } - updateTraction(nextTransform.getOrigin()); - return; - } - - // check if this hit is obviously unsteppable - btVector3 hitFromBase = result.m_hitPointWorld - (startPosition - ((_radius + _halfHeight) * _upDirection)); - btScalar hitHeight = hitFromBase.dot(_upDirection); - if (hitHeight > _maxStepHeight) { - // shape can't step over the obstacle so move forward as much as possible before we bail - btVector3 forwardTranslation = result.m_closestHitFraction * forwardSweep; - btScalar forwardDistance = forwardTranslation.length(); - if (forwardDistance > stepDistance) { - forwardTranslation *= stepDistance / forwardDistance; - } - nextTransform.setOrigin(startPosition + forwardTranslation); - setWorldTransform(nextTransform); - _onFloor = _onFloor || oldOnFloor; - return; - } - // if we get here then we hit something that might be steppable - - // remember the forward sweep hit fraction for later - btScalar forwardSweepHitFraction = result.m_closestHitFraction; - - // figure out how high we can step up - btScalar availableStepHeight = measureAvailableStepHeight(); - - // raise by availableStepHeight before sweeping forward - result.resetHitHistory(); - startTransform.setOrigin(startPosition + availableStepHeight * _upDirection); - nextTransform.setOrigin(startTransform.getOrigin() + forwardSweep); - sweepTest(_characterShape, startTransform, nextTransform, result); - if (result.hasHit()) { - startTransform.setOrigin(startTransform.getOrigin() + result.m_closestHitFraction * forwardSweep); - } else { - startTransform = nextTransform; - } - - // sweep down in search of future landing spot - result.resetHitHistory(); - btVector3 downSweep = (- availableStepHeight) * _upDirection; - nextTransform.setOrigin(startTransform.getOrigin() + downSweep); - sweepTest(_characterShape, startTransform, nextTransform, result); - if (result.hasHit() && result.m_hitNormalWorld.dot(_upDirection) > _maxWallNormalUpComponent) { - // can stand on future landing spot, so we interpolate toward it - _floorNormal = result.m_hitNormalWorld; - _floorContact = result.m_hitPointWorld; - _steppingUp = true; - _onFloor = true; - _hovering = false; - nextTransform.setOrigin(startTransform.getOrigin() + result.m_closestHitFraction * downSweep); - btVector3 totalStep = nextTransform.getOrigin() - startPosition; - nextTransform.setOrigin(startPosition + (stepDistance / totalStep.length()) * totalStep); - updateTraction(nextTransform.getOrigin()); - } else { - // either there is no future landing spot, or there is but we can't stand on it - // in any case: we go forward as much as possible - nextTransform.setOrigin(startPosition + forwardSweepHitFraction * (stepDistance / longSweepDistance) * forwardSweep); - _onFloor = _onFloor || oldOnFloor; - updateTraction(nextTransform.getOrigin()); - } - setWorldTransform(nextTransform); -} - -bool CharacterGhostObject::sweepTest( - const btConvexShape* shape, - const btTransform& start, - const btTransform& end, - CharacterSweepResult& result) const { - if (_world && _inWorld) { - assert(shape); - btScalar allowedPenetration = _world->getDispatchInfo().m_allowedCcdPenetration; - convexSweepTest(shape, start, end, result, allowedPenetration); - return result.hasHit(); - } - return false; -} - -bool CharacterGhostObject::rayTest(const btVector3& start, - const btVector3& end, - CharacterRayResult& result) const { - if (_world && _inWorld) { - _world->rayTest(start, end, result); - } - return result.hasHit(); -} - -void CharacterGhostObject::measurePenetration(btVector3& minBoxOut, btVector3& maxBoxOut) { - // minBoxOut and maxBoxOut will be updated with penetration envelope. - // If one of the corner points is <0,0,0> then the penetration is resolvable in a single step, - // but if the space spanned by the two corners extends in both directions along at least one - // component then we the object is sandwiched between two opposing objects. - - // We assume this object has just been moved to its current location, or else objects have been - // moved around it since the last step so we must update the overlapping pairs. - refreshOverlappingPairCache(); - - // compute collision details - btHashedOverlappingPairCache* pairCache = getOverlappingPairCache(); - _world->getDispatcher()->dispatchAllCollisionPairs(pairCache, _world->getDispatchInfo(), _world->getDispatcher()); - - // loop over contact manifolds to compute the penetration box - minBoxOut = btVector3(0.0f, 0.0f, 0.0f); - maxBoxOut = btVector3(0.0f, 0.0f, 0.0f); - btManifoldArray manifoldArray; - - int numPairs = pairCache->getNumOverlappingPairs(); - for (int i = 0; i < numPairs; i++) { - manifoldArray.resize(0); - btBroadphasePair* collisionPair = &(pairCache->getOverlappingPairArray()[i]); - - btCollisionObject* obj0 = static_cast(collisionPair->m_pProxy0->m_clientObject); - btCollisionObject* obj1 = static_cast(collisionPair->m_pProxy1->m_clientObject); - - if ((obj0 && !obj0->hasContactResponse()) && (obj1 && !obj1->hasContactResponse())) { - // we know this probe has no contact response - // but neither does the other object so skip this manifold - continue; - } - - if (!collisionPair->m_algorithm) { - // null m_algorithm means the two shape types don't know how to collide! - // shouldn't fall in here but just in case - continue; - } - - btScalar mostFloorPenetration = 0.0f; - collisionPair->m_algorithm->getAllContactManifolds(manifoldArray); - for (int j = 0; j < manifoldArray.size(); j++) { - btPersistentManifold* manifold = manifoldArray[j]; - btScalar directionSign = (manifold->getBody0() == this) ? btScalar(1.0) : btScalar(-1.0); - for (int p = 0; p < manifold->getNumContacts(); p++) { - const btManifoldPoint& pt = manifold->getContactPoint(p); - if (pt.getDistance() > 0.0f) { - continue; - } - - // normal always points from object to character - btVector3 normal = directionSign * pt.m_normalWorldOnB; - - btScalar penetrationDepth = pt.getDistance(); - if (penetrationDepth < mostFloorPenetration) { // remember penetrationDepth is negative - btScalar normalDotUp = normal.dot(_upDirection); - if (normalDotUp > _maxWallNormalUpComponent) { - mostFloorPenetration = penetrationDepth; - _floorNormal = normal; - if (directionSign > 0.0f) { - _floorContact = pt.m_positionWorldOnA; - } else { - _floorContact = pt.m_positionWorldOnB; - } - _onFloor = true; - } - } - - btVector3 penetration = (-penetrationDepth) * normal; - minBoxOut.setMin(penetration); - maxBoxOut.setMax(penetration); - } - } - } -} - -void CharacterGhostObject::refreshOverlappingPairCache() { - assert(_world && _inWorld); - btVector3 minAabb, maxAabb; - getCollisionShape()->getAabb(getWorldTransform(), minAabb, maxAabb); - // this updates both pairCaches: world broadphase and ghostobject - _world->getBroadphase()->setAabb(getBroadphaseHandle(), minAabb, maxAabb, _world->getDispatcher()); -} - -void CharacterGhostObject::removeFromWorld() { - if (_world && _inWorld) { - _world->removeCollisionObject(this); - _inWorld = false; - } -} - -void CharacterGhostObject::addToWorld() { - if (_world && !_inWorld) { - assert(getCollisionShape()); - setCollisionFlags(getCollisionFlags() | btCollisionObject::CF_NO_CONTACT_RESPONSE); - _world->addCollisionObject(this, _collisionFilterGroup, _collisionFilterMask); - _inWorld = true; - } -} - -bool CharacterGhostObject::resolvePenetration(int numTries) { - btVector3 minBox, maxBox; - measurePenetration(minBox, maxBox); - btVector3 restore = maxBox + minBox; - if (restore.length2() > 0.0f) { - btTransform transform = getWorldTransform(); - transform.setOrigin(transform.getOrigin() + restore); - setWorldTransform(transform); - return false; - } - return true; -} - -void CharacterGhostObject::updateVelocity(btScalar dt, btScalar gravity) { - if (!_motorOnly) { - if (_hovering) { - _linearVelocity *= 0.999f; // HACK damping - } else { - _linearVelocity += (dt * gravity) * _upDirection; - } - } -} - -void CharacterGhostObject::updateHoverState(const btVector3& position) { - if (_onFloor) { - _hovering = false; - } else { - // cast a ray down looking for floor support - CharacterRayResult rayResult(this); - btScalar distanceToFeet = _radius + _halfHeight; - btScalar slop = 2.0f * getCollisionShape()->getMargin(); // slop to help ray start OUTSIDE the floor object - btVector3 startPos = position - ((distanceToFeet - slop) * _upDirection); - btVector3 endPos = startPos - (2.0f * distanceToFeet) * _upDirection; - rayTest(startPos, endPos, rayResult); - // we're hovering if the ray didn't hit anything or hit unstandable slope - _hovering = !rayResult.hasHit() || rayResult.m_hitNormalWorld.dot(_upDirection) < _maxWallNormalUpComponent; - } -} - -void CharacterGhostObject::updateTraction(const btVector3& position) { - updateHoverState(position); - if (_hovering || _motorOnly) { - _linearVelocity = _motorVelocity; - } else if (_onFloor) { - // compute a velocity that swings the shape around the _floorContact - btVector3 leverArm = _floorContact - position; - btVector3 pathDirection = leverArm.cross(_motorVelocity.cross(leverArm)); - btScalar pathLength = pathDirection.length(); - if (pathLength > FLT_EPSILON) { - _linearVelocity = (_motorVelocity.length() / pathLength) * pathDirection; - } else { - _linearVelocity = btVector3(0.0f, 0.0f, 0.0f); - } - } -} - -btScalar CharacterGhostObject::measureAvailableStepHeight() const { - CharacterSweepResult result(this); - btTransform transform = getWorldTransform(); - btTransform nextTransform = transform; - nextTransform.setOrigin(transform.getOrigin() + _maxStepHeight * _upDirection); - sweepTest(_characterShape, transform, nextTransform, result); - return result.m_closestHitFraction * _maxStepHeight; -} - diff --git a/libraries/physics/src/CharacterGhostObject.h b/libraries/physics/src/CharacterGhostObject.h deleted file mode 100755 index feb132a53e..0000000000 --- a/libraries/physics/src/CharacterGhostObject.h +++ /dev/null @@ -1,103 +0,0 @@ -// -// CharacterGhostObject.h -// libraries/physics/src -// -// Created by Andrew Meadows 2016.08.26 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_CharacterGhostObject_h -#define hifi_CharacterGhostObject_h - -#include - -#include -#include -#include - -#include "CharacterSweepResult.h" -#include "CharacterRayResult.h" - -class CharacterGhostShape; - -class CharacterGhostObject : public btPairCachingGhostObject { -public: - CharacterGhostObject() { } - ~CharacterGhostObject(); - - void setCollisionGroupAndMask(int16_t group, int16_t mask); - void getCollisionGroupAndMask(int16_t& group, int16_t& mask) const; - - void setRadiusAndHalfHeight(btScalar radius, btScalar halfHeight); - void setUpDirection(const btVector3& up); - void setMotorVelocity(const btVector3& velocity); - void setMinWallAngle(btScalar angle) { _maxWallNormalUpComponent = cosf(angle); } - void setMaxStepHeight(btScalar height) { _maxStepHeight = height; } - - void setLinearVelocity(const btVector3& velocity) { _linearVelocity = velocity; } - const btVector3& getLinearVelocity() const { return _linearVelocity; } - - void setCharacterShape(btConvexHullShape* shape); - - void setCollisionWorld(btCollisionWorld* world); - - void move(btScalar dt, btScalar overshoot, btScalar gravity); - - bool sweepTest(const btConvexShape* shape, - const btTransform& start, - const btTransform& end, - CharacterSweepResult& result) const; - - bool rayTest(const btVector3& start, - const btVector3& end, - CharacterRayResult& result) const; - - bool isHovering() const { return _hovering; } - void setHovering(bool hovering) { _hovering = hovering; } - void setMotorOnly(bool motorOnly) { _motorOnly = motorOnly; } - - bool hasSupport() const { return _onFloor; } - bool isSteppingUp() const { return _steppingUp; } - const btVector3& getFloorNormal() const { return _floorNormal; } - - void measurePenetration(btVector3& minBoxOut, btVector3& maxBoxOut); - void refreshOverlappingPairCache(); - -protected: - void removeFromWorld(); - void addToWorld(); - - bool resolvePenetration(int numTries); - void updateVelocity(btScalar dt, btScalar gravity); - void updateTraction(const btVector3& position); - btScalar measureAvailableStepHeight() const; - void updateHoverState(const btVector3& position); - -protected: - btVector3 _upDirection { 0.0f, 1.0f, 0.0f }; // input, up in world-frame - btVector3 _motorVelocity { 0.0f, 0.0f, 0.0f }; // input, velocity character is trying to achieve - btVector3 _linearVelocity { 0.0f, 0.0f, 0.0f }; // internal, actual character velocity - btVector3 _floorNormal { 0.0f, 0.0f, 0.0f }; // internal, probable floor normal - btVector3 _floorContact { 0.0f, 0.0f, 0.0f }; // internal, last floor contact point - btCollisionWorld* _world { nullptr }; // input, pointer to world - //btScalar _distanceToFeet { 0.0f }; // input, distance from object center to lowest point on shape - btScalar _halfHeight { 0.0f }; - btScalar _radius { 0.0f }; - btScalar _maxWallNormalUpComponent { 0.0f }; // input: max vertical component of wall normal - btScalar _maxStepHeight { 0.0f }; // input, max step height the character can climb - btConvexHullShape* _characterShape { nullptr }; // input, shape of character - CharacterGhostShape* _ghostShape { nullptr }; // internal, shape whose Aabb is used for overlap cache - int16_t _collisionFilterGroup { 0 }; - int16_t _collisionFilterMask { 0 }; - bool _inWorld { false }; // internal, was added to world - bool _hovering { false }; // internal, - bool _onFloor { false }; // output, is actually standing on floor - bool _steppingUp { false }; // output, future sweep hit a steppable ledge - bool _hasFloor { false }; // output, has floor underneath to fall on - bool _motorOnly { false }; // input, _linearVelocity slaves to _motorVelocity -}; - -#endif // hifi_CharacterGhostObject_h diff --git a/libraries/physics/src/CharacterGhostShape.cpp b/libraries/physics/src/CharacterGhostShape.cpp deleted file mode 100644 index 09f4f0b80f..0000000000 --- a/libraries/physics/src/CharacterGhostShape.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// -// CharacterGhostShape.cpp -// libraries/physics/src -// -// Created by Andrew Meadows 2016.09.14 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "CharacterGhostShape.h" - -#include - - -CharacterGhostShape::CharacterGhostShape(const btConvexHullShape* shape) : - btConvexHullShape(reinterpret_cast(shape->getUnscaledPoints()), shape->getNumPoints(), sizeof(btVector3)) { - assert(shape); - assert(shape->getUnscaledPoints()); - assert(shape->getNumPoints() > 0); - setMargin(shape->getMargin()); -} - -void CharacterGhostShape::getAabb (const btTransform& t, btVector3& aabbMin, btVector3& aabbMax) const { - btConvexHullShape::getAabb(t, aabbMin, aabbMax); - // double the size of the Aabb by expanding both corners by half the extent - btVector3 expansion = 0.5f * (aabbMax - aabbMin); - aabbMin -= expansion; - aabbMax += expansion; -} diff --git a/libraries/physics/src/CharacterGhostShape.h b/libraries/physics/src/CharacterGhostShape.h deleted file mode 100644 index dc75c148d5..0000000000 --- a/libraries/physics/src/CharacterGhostShape.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// CharacterGhostShape.h -// libraries/physics/src -// -// Created by Andrew Meadows 2016.09.14 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_CharacterGhostShape_h -#define hifi_CharacterGhostShape_h - -#include - -class CharacterGhostShape : public btConvexHullShape { - // Same as btConvexHullShape but reports an expanded Aabb for larger ghost overlap cache -public: - CharacterGhostShape(const btConvexHullShape* shape); - - virtual void getAabb (const btTransform& t, btVector3& aabbMin, btVector3& aabbMax) const override; -}; - -#endif // hifi_CharacterGhostShape_h diff --git a/libraries/physics/src/CharacterRayResult.cpp b/libraries/physics/src/CharacterRayResult.cpp deleted file mode 100755 index 7a81e9cca6..0000000000 --- a/libraries/physics/src/CharacterRayResult.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// -// CharaterRayResult.cpp -// libraries/physics/src -// -// Created by Andrew Meadows 2016.09.05 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "CharacterRayResult.h" - -#include - -#include "CharacterGhostObject.h" - -CharacterRayResult::CharacterRayResult (const CharacterGhostObject* character) : - btCollisionWorld::ClosestRayResultCallback(btVector3(0.0f, 0.0f, 0.0f), btVector3(0.0f, 0.0f, 0.0f)), - _character(character) -{ - assert(_character); - _character->getCollisionGroupAndMask(m_collisionFilterGroup, m_collisionFilterMask); -} - -btScalar CharacterRayResult::addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) { - if (rayResult.m_collisionObject == _character) { - return 1.0f; - } - return ClosestRayResultCallback::addSingleResult (rayResult, normalInWorldSpace); -} diff --git a/libraries/physics/src/CharacterRayResult.h b/libraries/physics/src/CharacterRayResult.h deleted file mode 100644 index e8b0bb7f99..0000000000 --- a/libraries/physics/src/CharacterRayResult.h +++ /dev/null @@ -1,44 +0,0 @@ -// -// CharaterRayResult.h -// libraries/physics/src -// -// Created by Andrew Meadows 2016.09.05 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_CharacterRayResult_h -#define hifi_CharacterRayResult_h - -#include -#include - -class CharacterGhostObject; - -class CharacterRayResult : public btCollisionWorld::ClosestRayResultCallback { -public: - CharacterRayResult (const CharacterGhostObject* character); - - virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) override; - -protected: - const CharacterGhostObject* _character; - - // Note: Public data members inherited from ClosestRayResultCallback - // - // btVector3 m_rayFromWorld;//used to calculate hitPointWorld from hitFraction - // btVector3 m_rayToWorld; - // btVector3 m_hitNormalWorld; - // btVector3 m_hitPointWorld; - // - // Note: Public data members inherited from RayResultCallback - // - // btScalar m_closestHitFraction; - // const btCollisionObject* m_collisionObject; - // short int m_collisionFilterGroup; - // short int m_collisionFilterMask; -}; - -#endif // hifi_CharacterRayResult_h diff --git a/libraries/physics/src/CharacterSweepResult.cpp b/libraries/physics/src/CharacterSweepResult.cpp deleted file mode 100755 index a5c4092b1d..0000000000 --- a/libraries/physics/src/CharacterSweepResult.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// -// CharaterSweepResult.cpp -// libraries/physics/src -// -// Created by Andrew Meadows 2016.09.01 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "CharacterSweepResult.h" - -#include - -#include "CharacterGhostObject.h" - -CharacterSweepResult::CharacterSweepResult(const CharacterGhostObject* character) - : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0f, 0.0f, 0.0f), btVector3(0.0f, 0.0f, 0.0f)), - _character(character) -{ - // set collision group and mask to match _character - assert(_character); - _character->getCollisionGroupAndMask(m_collisionFilterGroup, m_collisionFilterMask); -} - -btScalar CharacterSweepResult::addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool useWorldFrame) { - // skip objects that we shouldn't collide with - if (!convexResult.m_hitCollisionObject->hasContactResponse()) { - return btScalar(1.0); - } - if (convexResult.m_hitCollisionObject == _character) { - return btScalar(1.0); - } - - return ClosestConvexResultCallback::addSingleResult(convexResult, useWorldFrame); -} - -void CharacterSweepResult::resetHitHistory() { - m_hitCollisionObject = nullptr; - m_closestHitFraction = btScalar(1.0f); -} diff --git a/libraries/physics/src/CharacterSweepResult.h b/libraries/physics/src/CharacterSweepResult.h deleted file mode 100644 index 1e2898a3cf..0000000000 --- a/libraries/physics/src/CharacterSweepResult.h +++ /dev/null @@ -1,45 +0,0 @@ -// -// CharaterSweepResult.h -// libraries/physics/src -// -// Created by Andrew Meadows 2016.09.01 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_CharacterSweepResult_h -#define hifi_CharacterSweepResult_h - -#include -#include - - -class CharacterGhostObject; - -class CharacterSweepResult : public btCollisionWorld::ClosestConvexResultCallback { -public: - CharacterSweepResult(const CharacterGhostObject* character); - virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool useWorldFrame) override; - void resetHitHistory(); -protected: - const CharacterGhostObject* _character; - - // NOTE: Public data members inherited from ClosestConvexResultCallback: - // - // btVector3 m_convexFromWorld; // unused except by btClosestNotMeConvexResultCallback - // btVector3 m_convexToWorld; // unused except by btClosestNotMeConvexResultCallback - // btVector3 m_hitNormalWorld; - // btVector3 m_hitPointWorld; - // const btCollisionObject* m_hitCollisionObject; - // - // NOTE: Public data members inherited from ConvexResultCallback: - // - // btScalar m_closestHitFraction; - // short int m_collisionFilterGroup; - // short int m_collisionFilterMask; - -}; - -#endif // hifi_CharacterSweepResult_h diff --git a/libraries/physics/src/CollisionRenderMeshCache.cpp b/libraries/physics/src/CollisionRenderMeshCache.cpp index 40a8a4aff9..3a1c4d0ea4 100644 --- a/libraries/physics/src/CollisionRenderMeshCache.cpp +++ b/libraries/physics/src/CollisionRenderMeshCache.cpp @@ -1,6 +1,6 @@ // // CollisionRenderMeshCache.cpp -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2016.07.13 // Copyright 2016 High Fidelity, Inc. diff --git a/libraries/physics/src/CollisionRenderMeshCache.h b/libraries/physics/src/CollisionRenderMeshCache.h index 6a6857a5ae..910b43996e 100644 --- a/libraries/physics/src/CollisionRenderMeshCache.h +++ b/libraries/physics/src/CollisionRenderMeshCache.h @@ -1,6 +1,6 @@ // // CollisionRenderMeshCache.h -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2016.07.13 // Copyright 2016 High Fidelity, Inc. diff --git a/libraries/physics/src/ContactInfo.cpp b/libraries/physics/src/ContactInfo.cpp index 9acf105d3a..c2ea6e8671 100644 --- a/libraries/physics/src/ContactInfo.cpp +++ b/libraries/physics/src/ContactInfo.cpp @@ -1,6 +1,6 @@ // // ContactEvent.cpp -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2015.01.20 // Copyright 2015 High Fidelity, Inc. diff --git a/libraries/physics/src/ContactInfo.h b/libraries/physics/src/ContactInfo.h index bbd2fdb460..11c908a414 100644 --- a/libraries/physics/src/ContactInfo.h +++ b/libraries/physics/src/ContactInfo.h @@ -1,6 +1,6 @@ // // ContactEvent.h -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2015.01.20 // Copyright 2015 High Fidelity, Inc. diff --git a/libraries/physics/src/ObjectAction.cpp b/libraries/physics/src/ObjectAction.cpp index 0d5948a793..ca64cabe5f 100644 --- a/libraries/physics/src/ObjectAction.cpp +++ b/libraries/physics/src/ObjectAction.cpp @@ -1,6 +1,6 @@ // // ObjectAction.cpp -// libraries/physics/src +// libraries/physcis/src // // Created by Seth Alves 2015-6-2 // Copyright 2015 High Fidelity, Inc. diff --git a/libraries/physics/src/ObjectAction.h b/libraries/physics/src/ObjectAction.h index 1327fd7c6f..43330269ac 100644 --- a/libraries/physics/src/ObjectAction.h +++ b/libraries/physics/src/ObjectAction.h @@ -1,6 +1,6 @@ // // ObjectAction.h -// libraries/physics/src +// libraries/physcis/src // // Created by Seth Alves 2015-6-2 // Copyright 2015 High Fidelity, Inc. diff --git a/libraries/physics/src/ObjectMotionState.cpp b/libraries/physics/src/ObjectMotionState.cpp index 503b39dc1c..38f079c1d4 100644 --- a/libraries/physics/src/ObjectMotionState.cpp +++ b/libraries/physics/src/ObjectMotionState.cpp @@ -1,6 +1,6 @@ // // ObjectMotionState.cpp -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2014.11.05 // Copyright 2014 High Fidelity, Inc. diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 17d75c733b..a7894998a8 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -1,6 +1,6 @@ // // ObjectMotionState.h -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2014.11.05 // Copyright 2014 High Fidelity, Inc. diff --git a/libraries/physics/src/PhysicalEntitySimulation.cpp b/libraries/physics/src/PhysicalEntitySimulation.cpp index dbef9b1f7d..903b160a5e 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.cpp +++ b/libraries/physics/src/PhysicalEntitySimulation.cpp @@ -1,6 +1,6 @@ // // PhysicalEntitySimulation.cpp -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2015.04.27 // Copyright 2015 High Fidelity, Inc. diff --git a/libraries/physics/src/PhysicalEntitySimulation.h b/libraries/physics/src/PhysicalEntitySimulation.h index 79d16604c9..24b9ba95f0 100644 --- a/libraries/physics/src/PhysicalEntitySimulation.h +++ b/libraries/physics/src/PhysicalEntitySimulation.h @@ -1,6 +1,6 @@ // // PhysicalEntitySimulation.h -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2015.04.27 // Copyright 2015 High Fidelity, Inc. diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index fa626d0c91..ba002d925c 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -1,6 +1,6 @@ // // PhysicsEngine.cpp -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2014.10.29 // Copyright 2014 High Fidelity, Inc. diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index ae78802a64..bbafbb06b6 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -1,6 +1,6 @@ // // PhysicsEngine.h -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2014.10.29 // Copyright 2014 High Fidelity, Inc. diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index 5268278be0..100dab0fd1 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -1,6 +1,6 @@ // // ShapeFactory.cpp -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2014.12.01 // Copyright 2014 High Fidelity, Inc. diff --git a/libraries/physics/src/ShapeFactory.h b/libraries/physics/src/ShapeFactory.h index 2bf79f390c..a1022104dd 100644 --- a/libraries/physics/src/ShapeFactory.h +++ b/libraries/physics/src/ShapeFactory.h @@ -1,6 +1,6 @@ // // ShapeFactory.h -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2014.12.01 // Copyright 2014 High Fidelity, Inc. diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp index fd3e35d28a..b61fb0037b 100644 --- a/libraries/physics/src/ShapeManager.cpp +++ b/libraries/physics/src/ShapeManager.cpp @@ -1,6 +1,6 @@ // // ShapeManager.cpp -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2014.10.29 // Copyright 2014 High Fidelity, Inc. diff --git a/libraries/physics/src/ShapeManager.h b/libraries/physics/src/ShapeManager.h index ed81b5e8f8..261c06ddb9 100644 --- a/libraries/physics/src/ShapeManager.h +++ b/libraries/physics/src/ShapeManager.h @@ -1,6 +1,6 @@ // // ShapeManager.h -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2014.10.29 // Copyright 2014 High Fidelity, Inc. diff --git a/libraries/shared/src/BackgroundMode.h b/libraries/shared/src/BackgroundMode.h index 0e0d684e62..e6e585d9d8 100644 --- a/libraries/shared/src/BackgroundMode.h +++ b/libraries/shared/src/BackgroundMode.h @@ -1,6 +1,6 @@ // // BackgroundMode.h -// libraries/physics/src +// libraries/physcis/src // // Copyright 2015 High Fidelity, Inc. // diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index 91623f1e83..92fe138021 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -578,42 +578,3 @@ float coneSphereAngle(const glm::vec3& coneCenter, const glm::vec3& coneDirectio return glm::max(0.0f, theta - phi); } - -glm::vec3 nearestPointOnLineSegment(const glm::vec3& point, const glm::vec3& segmentStart, const glm::vec3& segmentEnd) { - glm::vec3 n = segmentEnd - segmentStart; - float len = glm::length(n); - if (len < EPSILON) { - return segmentStart; - } else { - n /= len; - float projection = glm::dot(point - segmentStart, n); - projection = glm::clamp(projection, 0.0f, len); - return segmentStart + n * projection; - } -} - -float distanceFromLineSegment(const glm::vec3& point, const glm::vec3& segmentStart, const glm::vec3& segmentEnd) { - return glm::length(point - nearestPointOnLineSegment(point, segmentStart, segmentEnd)); -} - -float distanceFromCapsule(const glm::vec3& point, const glm::vec3& segmentStart, const glm::vec3& segmentEnd, float capsuleRadius) { - return distanceFromLineSegment(point, segmentStart, segmentEnd) - capsuleRadius; -} - -bool pointIsInsideCapsule(const glm::vec3& point, const glm::vec3& capsuleStart, const glm::vec3& capsuleEnd, float capsuleRadius) { - return distanceFromLineSegment(point, capsuleStart, capsuleEnd) < capsuleRadius; -} - -glm::vec3 projectPointOntoCapsule(const glm::vec3& point, const glm::vec3& capsuleStart, const glm::vec3& capsuleEnd, float capsuleRadius) { - glm::vec3 nearestPoint = nearestPointOnLineSegment(point, capsuleStart, capsuleEnd); - glm::vec3 d = point - nearestPoint; - float dLen = glm::length(d); - if (dLen < EPSILON) { - return nearestPoint; // TODO: maybe we should pick a point actually on the surface... - } else { - return nearestPoint + d * (capsuleRadius / dLen); - } -} - - - diff --git a/libraries/shared/src/GeometryUtil.h b/libraries/shared/src/GeometryUtil.h index 47f069931d..1c951ca09a 100644 --- a/libraries/shared/src/GeometryUtil.h +++ b/libraries/shared/src/GeometryUtil.h @@ -160,10 +160,5 @@ private: static void copyCleanArray(int& lengthA, glm::vec2* vertexArrayA, int& lengthB, glm::vec2* vertexArrayB); }; -glm::vec3 nearestPointOnLineSegment(const glm::vec3& point, const glm::vec3& segmentStart, const glm::vec3& segmentEnd); -float distanceFromLineSegment(const glm::vec3& point, const glm::vec3& segmentStart, const glm::vec3& segmentEnd); -float distanceFromCapsule(const glm::vec3& point, const glm::vec3& segmentStart, const glm::vec3& segmentEnd, float capsuleRadius); -bool pointIsInsideCapsule(const glm::vec3& point, const glm::vec3& capsuleStart, const glm::vec3& capsuleEnd, float capsuleRadius); -glm::vec3 projectPointOntoCapsule(const glm::vec3& point, const glm::vec3& capsuleStart, const glm::vec3& capsuleEnd, float capsuleRadius); #endif // hifi_GeometryUtil_h diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index 583bceeaf2..b8ea3a4272 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -1,6 +1,6 @@ // // ShapeInfo.cpp -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2014.10.29 // Copyright 2014 High Fidelity, Inc. diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h index 732b62ebbf..a6ff8d6d4a 100644 --- a/libraries/shared/src/ShapeInfo.h +++ b/libraries/shared/src/ShapeInfo.h @@ -1,6 +1,6 @@ // // ShapeInfo.h -// libraries/physics/src +// libraries/physcis/src // // Created by Andrew Meadows 2014.10.29 // Copyright 2014 High Fidelity, Inc. diff --git a/scripts/system/controllers/teleport.js b/scripts/system/controllers/teleport.js index ffead2eb6b..d3284352bf 100644 --- a/scripts/system/controllers/teleport.js +++ b/scripts/system/controllers/teleport.js @@ -13,7 +13,7 @@ var inTeleportMode = false; var SMOOTH_ARRIVAL_SPACING = 33; -var NUMBER_OF_STEPS_FOR_TELEPORT = 6; +var NUMBER_OF_STEPS = 6; var TARGET_MODEL_URL = Script.resolvePath("../assets/models/teleport-destination.fbx"); var TOO_CLOSE_MODEL_URL = Script.resolvePath("../assets/models/teleport-cancel.fbx"); @@ -44,7 +44,6 @@ var COLORS_TELEPORT_TOO_CLOSE = { var TELEPORT_CANCEL_RANGE = 1; var USE_COOL_IN = true; var COOL_IN_DURATION = 500; -var MAX_HMD_AVATAR_SEPARATION = 10.0; function ThumbPad(hand) { this.hand = hand; @@ -89,7 +88,6 @@ function Teleporter() { this.updateConnected = null; this.smoothArrivalInterval = null; this.teleportHand = null; - this.teleportMode = "HMDAndAvatarTogether"; this.tooClose = false; this.inCoolIn = false; @@ -545,68 +543,43 @@ function Teleporter() { var offset = getAvatarFootOffset(); this.intersection.intersection.y += offset; this.exitTeleportMode(); - if (MyAvatar.hmdLeanRecenterEnabled) { - if (Vec3.distance(MyAvatar.position, _this.intersection.intersection) > MAX_HMD_AVATAR_SEPARATION) { - this.teleportMode = "HMDAndAvatarTogether"; - } else { - this.teleportMode = "HMDFirstAvatarWillFollow"; - } - } else { - this.teleportMode = "AvatarOnly"; - } - // Disable smooth arrival, possibly temporarily //this.smoothArrival(); - // Instead jump to the intersection directly. - var landingPoint = _this.intersection.intersection; - _this.teleportTo(landingPoint); - if (this.teleportMode === "HMDFirstAvatarWillFollow") { - MyAvatar.position = landingPoint; - } - - // cleanup UI + MyAvatar.position = _this.intersection.intersection; _this.hideTargetOverlay(); _this.hideCancelOverlay(); HMD.centerUI(); } }; - this.getWayPoints = function(startPoint, endPoint, numberOfSteps) { - var travel = Vec3.subtract(endPoint, startPoint); - var distance = Vec3.length(travel); - var wayPoints = []; - if (distance > 1.0) { - var base = Math.exp(Math.log(distance + 1.0) / numberOfSteps); - var i; - - // this fancy math generates regular points in logarithmic space - for (i = 0; i < numberOfSteps - 1; i++) { - var backFraction = (1.0 - Math.exp((numberOfSteps - 1 - i) * Math.log(base))) / distance; - wayPoints.push(Vec3.sum(endPoint, Vec3.multiply(backFraction, travel))); - } - } - wayPoints.push(endPoint); - return wayPoints; + this.findMidpoint = function(start, end) { + var xy = Vec3.sum(start, end); + var midpoint = Vec3.multiply(0.5, xy); + return midpoint }; - this.teleportTo = function(landingPoint) { - if (this.teleportMode === "AvatarOnly") { - MyAvatar.position = landingPoint; - } else if (this.teleportMode === "HMDAndAvatarTogether") { - var leanEnabled = MyAvatar.hmdLeanRecenterEnabled; - MyAvatar.setHMDLeanRecenterEnabled(false); - MyAvatar.position = landingPoint; - HMD.snapToAvatar(); - MyAvatar.hmdLeanRecenterEnabled = leanEnabled; - } else if (this.teleportMode === "HMDFirstAvatarWillFollow") { - var eyeOffset = Vec3.subtract(MyAvatar.getEyePosition(), MyAvatar.position); - landingPoint = Vec3.sum(landingPoint, eyeOffset); - HMD.position = landingPoint; + this.getArrivalPoints = function(startPoint, endPoint) { + var arrivalPoints = []; + var i; + var lastPoint; + + for (i = 0; i < NUMBER_OF_STEPS; i++) { + if (i === 0) { + lastPoint = startPoint; + } + var newPoint = _this.findMidpoint(lastPoint, endPoint); + lastPoint = newPoint; + arrivalPoints.push(newPoint); } - } + + arrivalPoints.push(endPoint); + + return arrivalPoints; + }; this.smoothArrival = function() { - _this.arrivalPoints = _this.getWayPoints(MyAvatar.position, _this.intersection.intersection, NUMBER_OF_STEPS_FOR_TELEPORT); + + _this.arrivalPoints = _this.getArrivalPoints(MyAvatar.position, _this.intersection.intersection); _this.smoothArrivalInterval = Script.setInterval(function() { if (_this.arrivalPoints.length === 0) { Script.clearInterval(_this.smoothArrivalInterval); @@ -614,7 +587,7 @@ function Teleporter() { return; } var landingPoint = _this.arrivalPoints.shift(); - _this.teleportTo(landingPoint); + MyAvatar.position = landingPoint; if (_this.arrivalPoints.length === 1 || _this.arrivalPoints.length === 0) { _this.hideTargetOverlay(); @@ -626,6 +599,7 @@ function Teleporter() { this.createTargetOverlay(false); this.createCancelOverlay(false); + } //related to repositioning the avatar after you teleport