diff --git a/examples/edit.js b/examples/edit.js index 52e565f3f9..2529b617d9 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -88,13 +88,7 @@ var SETTING_SHOW_LIGHTS_IN_EDIT_MODE = "showLightsInEditMode"; var INSUFFICIENT_PERMISSIONS_ERROR_MSG = "You do not have the necessary permissions to edit on this domain." var modelURLs = [ - HIFI_PUBLIC_BUCKET + "models/entities/2-Terrain:%20Alder.fbx", - HIFI_PUBLIC_BUCKET + "models/entities/2-Terrain:%20Bush1.fbx", - HIFI_PUBLIC_BUCKET + "models/entities/2-Terrain:%20Bush6.fbx", - HIFI_PUBLIC_BUCKET + "models/entities/3-Buildings-1-Rustic-Shed.fbx", - HIFI_PUBLIC_BUCKET + "models/entities/3-Buildings-1-Rustic-Shed2.fbx", - HIFI_PUBLIC_BUCKET + "models/entities/3-Buildings-1-Rustic-Shed4.fbx", - HIFI_PUBLIC_BUCKET + "models/entities/3-Buildings-1-Rustic-Shed7.fbx" + "Insert the URL to your FBX" ]; var mode = 0; @@ -387,9 +381,7 @@ var toolBar = (function () { position: grid.snapToSurface(grid.snapToGrid(position, false, DEFAULT_LIGHT_DIMENSIONS), DEFAULT_LIGHT_DIMENSIONS), dimensions: DEFAULT_LIGHT_DIMENSIONS, isSpotlight: false, - diffuseColor: { red: 255, green: 255, blue: 255 }, - ambientColor: { red: 255, green: 255, blue: 255 }, - specularColor: { red: 0, green: 0, blue: 0 }, + color: { red: 150, green: 150, blue: 150 }, constantAttenuation: 1, linearAttenuation: 0, diff --git a/interface/resources/html/interface-welcome.html b/interface/resources/html/interface-welcome.html index ed905eb392..26ae6ff5c0 100644 --- a/interface/resources/html/interface-welcome.html +++ b/interface/resources/html/interface-welcome.html @@ -138,7 +138,7 @@

Import models

- Use the editEntitles.js script to
+ Use the edit.js script to
add FBX models in-world. You
can use grids and fine tune
placement-related parameters
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a69fc3659d..619f664c21 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -796,7 +796,7 @@ void Application::paintGL() { DependencyManager::get()->render(); - if (Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)) { + { PerformanceTimer perfTimer("renderOverlay"); _applicationOverlay.renderOverlay(true); _applicationOverlay.displayOverlayTexture(); @@ -994,12 +994,6 @@ void Application::keyPressEvent(QKeyEvent* event) { resetSensors(); break; - case Qt::Key_G: - if (isShifted) { - Menu::getInstance()->triggerOption(MenuOption::ObeyEnvironmentalGravity); - } - break; - case Qt::Key_A: if (isShifted) { Menu::getInstance()->triggerOption(MenuOption::Atmosphere); @@ -1134,13 +1128,10 @@ void Application::keyPressEvent(QKeyEvent* event) { Menu::getInstance()->triggerOption(MenuOption::FullscreenMirror); } break; - case Qt::Key_Slash: - Menu::getInstance()->triggerOption(MenuOption::UserInterface); - break; case Qt::Key_P: Menu::getInstance()->triggerOption(MenuOption::FirstPerson); break; - case Qt::Key_Percent: + case Qt::Key_Slash: Menu::getInstance()->triggerOption(MenuOption::Stats); break; case Qt::Key_Plus: @@ -1175,10 +1166,6 @@ void Application::keyPressEvent(QKeyEvent* event) { break; } - case Qt::Key_Comma: { - _myAvatar->togglePhysicsEnabled(); - } - default: event->ignore(); break; @@ -2205,7 +2192,6 @@ void Application::update(float deltaTime) { { PerformanceTimer perfTimer("physics"); - _myAvatar->preSimulation(); _physicsEngine.stepSimulation(); } @@ -3029,8 +3015,7 @@ void Application::displaySide(Camera& theCamera, bool selfAvatarOnly, RenderArgs _nodeBoundsDisplay.draw(); // Render the world box - if (theCamera.getMode() != CAMERA_MODE_MIRROR && Menu::getInstance()->isOptionChecked(MenuOption::Stats) && - Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)) { + if (theCamera.getMode() != CAMERA_MODE_MIRROR && Menu::getInstance()->isOptionChecked(MenuOption::Stats)) { PerformanceTimer perfTimer("worldBox"); renderWorldBox(); } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 8d3c01320f..5867dd29e2 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -201,9 +201,7 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true); addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::GlowWhenSpeaking, 0, true); addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::BlueSpeechSphere, 0, true); - addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ObeyEnvironmentalGravity, Qt::SHIFT | Qt::Key_G, false, - avatar, SLOT(updateMotionBehavior())); - addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::StandOnNearbyFloors, 0, true, + addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::EnableCharacterController, 0, true, avatar, SLOT(updateMotionBehavior())); addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ShiftHipsForIdleAnimations, 0, false, avatar, SLOT(updateMotionBehavior())); @@ -230,7 +228,6 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::SHIFT | Qt::Key_H, true); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H, false, qApp, SLOT(cameraMenuChanged())); - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::UserInterface, Qt::Key_Slash, true); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools, Qt::META | Qt::Key_H, false, @@ -259,14 +256,13 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::TurnWithHead, 0, false); - addDisabledActionAndSeparator(viewMenu, "Stats"); - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Percent); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Slash); addActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L, qApp, SLOT(toggleLogDialog())); addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0, dialogsManager.data(), SLOT(bandwidthDetails())); addActionToQMenuAndActionHash(viewMenu, MenuOption::OctreeStats, 0, dialogsManager.data(), SLOT(octreeStatsDetails())); - addActionToQMenuAndActionHash(viewMenu, MenuOption::EditEntitiesHelp, 0, qApp, SLOT(showEditEntitiesHelp())); + QMenu* developerMenu = addMenu("Developer"); @@ -538,10 +534,12 @@ Menu::Menu() { statsRenderer.data(), SLOT(toggleShowInjectedStreams())); -#ifndef Q_OS_MAC QMenu* helpMenu = addMenu("Help"); - QAction* helpAction = helpMenu->addAction(MenuOption::AboutApp); - connect(helpAction, SIGNAL(triggered()), qApp, SLOT(aboutApp())); + addActionToQMenuAndActionHash(helpMenu, MenuOption::EditEntitiesHelp, 0, qApp, SLOT(showEditEntitiesHelp())); + +#ifndef Q_OS_MAC + QAction* aboutAction = helpMenu->addAction(MenuOption::AboutApp); + connect(aboutAction, SIGNAL(triggered()), qApp, SLOT(aboutApp())); #endif } diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 60fe5d4cd2..a4e644f20f 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -153,6 +153,7 @@ namespace MenuOption { const QString EchoServerAudio = "Echo Server Audio"; const QString EditEntitiesHelp = "Edit Entities Help..."; const QString Enable3DTVMode = "Enable 3DTV Mode"; + const QString EnableCharacterController = "Enable avatar collisions"; const QString EnableGlowEffect = "Enable Glow Effect (Warning: Poor Oculus Performance)"; const QString EnableVRMode = "Enable VR Mode"; const QString Entities = "Entities"; @@ -184,12 +185,9 @@ namespace MenuOption { const QString Mirror = "Mirror"; const QString MuteAudio = "Mute Microphone"; const QString MuteEnvironment = "Mute Environment"; - const QString NewVoxelCullingMode = "New Voxel Culling Mode"; const QString NoFaceTracking = "None"; - const QString ObeyEnvironmentalGravity = "Obey Environmental Gravity"; - const QString OctreeStats = "Voxel and Entity Statistics"; + const QString OctreeStats = "Entity Statistics"; const QString OffAxisProjection = "Off-Axis Projection"; - const QString OldVoxelCullingMode = "Old Voxel Culling Mode"; const QString OnlyDisplayTopTen = "Only Display Top Ten"; const QString Pair = "Pair"; const QString PipelineWarnings = "Log Render Pipeline Warnings"; @@ -233,13 +231,11 @@ namespace MenuOption { const QString ScriptEditor = "Script Editor..."; const QString ScriptedMotorControl = "Enable Scripted Motor Control"; const QString ShowBordersEntityNodes = "Show Entity Nodes"; - const QString ShowBordersVoxelNodes = "Show Voxel Nodes"; const QString ShowIKConstraints = "Show IK Constraints"; const QString SimpleShadows = "Simple"; const QString SixenseEnabled = "Enable Hydra Support"; const QString SixenseMouseInput = "Enable Sixense Mouse Input"; const QString SixenseLasers = "Enable Sixense UI Lasers"; - const QString StandOnNearbyFloors = "Stand on nearby floors"; const QString ShiftHipsForIdleAnimations = "Shift hips for idle animations"; const QString Stars = "Stars"; const QString Stats = "Stats"; @@ -251,7 +247,6 @@ namespace MenuOption { const QString TransmitterDrive = "Transmitter Drive"; const QString TurnWithHead = "Turn using Head"; const QString PackageModel = "Package Model"; - const QString UserInterface = "User Interface"; const QString Visage = "Visage"; const QString Wireframe = "Wireframe"; } diff --git a/interface/src/ModelPackager.cpp b/interface/src/ModelPackager.cpp index 49d4ae566f..f552d67a98 100644 --- a/interface/src/ModelPackager.cpp +++ b/interface/src/ModelPackager.cpp @@ -14,6 +14,8 @@ #include #include +#include + #include "ModelSelector.h" #include "ModelPropertiesDialog.h" diff --git a/interface/src/ModelPackager.h b/interface/src/ModelPackager.h index c62388f196..2c90395e56 100644 --- a/interface/src/ModelPackager.h +++ b/interface/src/ModelPackager.h @@ -15,8 +15,6 @@ #include #include -#include - #include "ui/ModelsBrowser.h" class ModelPackager : public QObject { diff --git a/interface/src/ModelPropertiesDialog.cpp b/interface/src/ModelPropertiesDialog.cpp index de98407a2a..81fe9ce7fd 100644 --- a/interface/src/ModelPropertiesDialog.cpp +++ b/interface/src/ModelPropertiesDialog.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include "ModelPropertiesDialog.h" diff --git a/interface/src/ModelPropertiesDialog.h b/interface/src/ModelPropertiesDialog.h index 65c5be6c21..5af4d173f1 100644 --- a/interface/src/ModelPropertiesDialog.h +++ b/interface/src/ModelPropertiesDialog.h @@ -23,19 +23,6 @@ class QComboBox; class QCheckBox; class QVBoxLayout; -static const QString NAME_FIELD = "name"; -static const QString FILENAME_FIELD = "filename"; -static const QString TEXDIR_FIELD = "texdir"; -static const QString LOD_FIELD = "lod"; -static const QString JOINT_INDEX_FIELD = "jointIndex"; -static const QString SCALE_FIELD = "scale"; -static const QString TRANSLATION_X_FIELD = "tx"; -static const QString TRANSLATION_Y_FIELD = "ty"; -static const QString TRANSLATION_Z_FIELD = "tz"; -static const QString JOINT_FIELD = "joint"; -static const QString FREE_JOINT_FIELD = "freeJoint"; -static const QString BLENDSHAPE_FIELD = "bs"; - /// A dialog that allows customization of various model properties. class ModelPropertiesDialog : public QDialog { Q_OBJECT diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 2f42544f28..26b777b35b 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -81,7 +81,6 @@ MyAvatar::MyAvatar() : _scriptedMotorTimescale(DEFAULT_SCRIPTED_MOTOR_TIMESCALE), _scriptedMotorFrame(SCRIPTED_MOTOR_CAMERA_FRAME), _motionBehaviors(AVATAR_MOTION_DEFAULTS), - _enablePhysics(false), _characterController(this), _lookAtTargetAvatar(), _shouldRender(true), @@ -101,6 +100,7 @@ MyAvatar::MyAvatar() : // connect to AddressManager signal for location jumps connect(DependencyManager::get().data(), &AddressManager::locationChangeRequired, this, &MyAvatar::goToLocation); + _characterController.setEnabled(true); } MyAvatar::~MyAvatar() { @@ -147,10 +147,6 @@ void MyAvatar::update(float deltaTime) { head->setAudioLoudness(audio->getLastInputLoudness()); head->setAudioAverageLoudness(audio->getAudioAverageInputLoudness()); - if (_motionBehaviors & AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY) { - setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition())); - } - simulate(deltaTime); if (_feetTouchFloor) { _skeletonModel.updateStandingFoot(); @@ -174,11 +170,7 @@ void MyAvatar::simulate(float deltaTime) { { PerformanceTimer perfTimer("transform"); updateOrientation(deltaTime); - if (isPhysicsEnabled()) { - updatePositionWithPhysics(deltaTime); - } else { - updatePosition(deltaTime); - } + updatePosition(deltaTime); } { @@ -484,26 +476,6 @@ void MyAvatar::loadLastRecording() { _player->loadRecording(_recorder->getRecording()); } -void MyAvatar::setLocalGravity(glm::vec3 gravity) { - _motionBehaviors |= AVATAR_MOTION_OBEY_LOCAL_GRAVITY; - // Environmental and Local gravities are incompatible. Since Local is being set here - // the environmental setting must be removed. - _motionBehaviors &= ~AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY; - setGravity(gravity); -} - -void MyAvatar::setGravity(const glm::vec3& gravity) { - _gravity = gravity; - - // use the gravity to determine the new world up direction, if possible - float gravityLength = glm::length(gravity); - if (gravityLength > EPSILON) { - _worldUpDirection = _gravity / -gravityLength; - } - // NOTE: the else case here it to leave _worldUpDirection unchanged - // so it continues to point opposite to the previous gravity setting. -} - AnimationHandlePointer MyAvatar::addAnimationHandle() { AnimationHandlePointer handle = _skeletonModel.createAnimationHandle(); _animationHandles.append(handle); @@ -1258,128 +1230,38 @@ glm::vec3 MyAvatar::applyScriptedMotor(float deltaTime, const glm::vec3& localVe return localVelocity + motorEfficiency * deltaVelocity; } -const float NEARBY_FLOOR_THRESHOLD = 5.0f; - void MyAvatar::updatePosition(float deltaTime) { - - // check for floor by casting a ray straight down from avatar's position - float heightAboveFloor = FLT_MAX; - bool hasFloor = false; - const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape(); - const float maxFloorDistance = boundingShape.getBoundingRadius() * NEARBY_FLOOR_THRESHOLD; - - RayIntersectionInfo intersection; - // NOTE: avatar is center of PhysicsSimulation, so rayStart is the origin for the purposes of the raycast - intersection._rayStart = glm::vec3(0.0f); - intersection._rayDirection = - _worldUpDirection; - intersection._rayLength = 4.0f * boundingShape.getBoundingRadius(); - - // velocity is initialized to the measured _velocity but will be modified by friction, external thrust, etc - glm::vec3 velocity = _velocity; - - bool pushingUp = (_driveKeys[UP] - _driveKeys[DOWN] > 0.0f) || _scriptedMotorVelocity.y > 0.0f; - if (_motionBehaviors & AVATAR_MOTION_STAND_ON_NEARBY_FLOORS) { - const float MAX_SPEED_UNDER_GRAVITY = 2.0f * _scale * MAX_WALKING_SPEED; - if (pushingUp || glm::length2(velocity) > MAX_SPEED_UNDER_GRAVITY * MAX_SPEED_UNDER_GRAVITY) { - // we're pushing up or moving quickly, so disable gravity - setLocalGravity(glm::vec3(0.0f)); - hasFloor = false; - } else { - if (heightAboveFloor > maxFloorDistance) { - // disable local gravity when floor is too far away - setLocalGravity(glm::vec3(0.0f)); - hasFloor = false; - } else { - // enable gravity - setLocalGravity(-_worldUpDirection); - } - } - } - - bool zeroDownwardVelocity = false; - bool gravityEnabled = (glm::length2(_gravity) > EPSILON); - if (gravityEnabled) { - const float SLOP = 0.002f; - if (heightAboveFloor < SLOP) { - if (heightAboveFloor < 0.0) { - // Gravity is in effect so we assume that the avatar is colliding against the world and we need - // to lift avatar out of floor, but we don't want to do it too fast (keep it smooth). - float distanceToLift = glm::min(-heightAboveFloor, MAX_WALKING_SPEED * deltaTime); - - // We don't use applyPositionDelta() for this lift distance because we don't want the avatar - // to come flying out of the floor. Instead we update position directly, and set a boolean - // that will remind us later to zero any downward component of the velocity. - _position += distanceToLift * _worldUpDirection; - } - zeroDownwardVelocity = true; - } - if (!zeroDownwardVelocity) { - velocity += (deltaTime * GRAVITY_EARTH) * _gravity; - } - } - - // rotate velocity into camera frame - glm::quat rotation = getHead()->getCameraOrientation(); - glm::vec3 localVelocity = glm::inverse(rotation) * velocity; - - // apply motors in camera frame - glm::vec3 newLocalVelocity = applyKeyboardMotor(deltaTime, localVelocity, hasFloor); - newLocalVelocity = applyScriptedMotor(deltaTime, newLocalVelocity); - - // rotate back into world-frame - velocity = rotation * newLocalVelocity; - - // apply thrust - velocity += _thrust * deltaTime; - _thrust = glm::vec3(0.0f); - - // remove downward velocity so we don't push into floor - if (zeroDownwardVelocity) { - float verticalSpeed = glm::dot(velocity, _worldUpDirection); - if (verticalSpeed < 0.0f || !pushingUp) { - velocity -= verticalSpeed * _worldUpDirection; - } - } - - // cap avatar speed - float speed = glm::length(velocity); - if (speed > MAX_AVATAR_SPEED) { - velocity *= MAX_AVATAR_SPEED / speed; - speed = MAX_AVATAR_SPEED; - } - - // update position - if (speed > MIN_AVATAR_SPEED) { - applyPositionDelta(deltaTime * velocity); - } - - // update _moving flag based on speed - const float MOVING_SPEED_THRESHOLD = 0.01f; - _moving = speed > MOVING_SPEED_THRESHOLD; - - measureMotionDerivatives(deltaTime); -} - -void MyAvatar::updatePositionWithPhysics(float deltaTime) { // rotate velocity into camera frame glm::quat rotation = getHead()->getCameraOrientation(); glm::vec3 localVelocity = glm::inverse(rotation) * _velocity; - bool hasFloor = false; - glm::vec3 newLocalVelocity = applyKeyboardMotor(deltaTime, localVelocity, hasFloor); + bool isOnGround = _characterController.onGround(); + glm::vec3 newLocalVelocity = applyKeyboardMotor(deltaTime, localVelocity, isOnGround); newLocalVelocity = applyScriptedMotor(deltaTime, newLocalVelocity); - // cap avatar speed - float speed = glm::length(newLocalVelocity); - if (speed > MAX_WALKING_SPEED) { - newLocalVelocity *= MAX_WALKING_SPEED / speed; - } - // rotate back into world-frame _velocity = rotation * newLocalVelocity; _velocity += _thrust * deltaTime; _thrust = glm::vec3(0.0f); + + // cap avatar speed + float speed = glm::length(_velocity); + if (speed > MAX_AVATAR_SPEED) { + _velocity *= MAX_AVATAR_SPEED / speed; + speed = MAX_AVATAR_SPEED; + } + + if (speed > MIN_AVATAR_SPEED && !_characterController.isEnabled()) { + // update position ourselves + applyPositionDelta(deltaTime * _velocity); + measureMotionDerivatives(deltaTime); + } // else physics will move avatar later + + // update _moving flag based on speed + const float MOVING_SPEED_THRESHOLD = 0.01f; + _moving = speed > MOVING_SPEED_THRESHOLD; + } void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTime, float frequency) { @@ -1496,23 +1378,6 @@ void MyAvatar::goToLocation(const glm::vec3& newPosition, void MyAvatar::updateMotionBehavior() { Menu* menu = Menu::getInstance(); - if (menu->isOptionChecked(MenuOption::ObeyEnvironmentalGravity)) { - _motionBehaviors |= AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY; - // Environmental and Local gravities are incompatible. Environmental setting trumps local. - _motionBehaviors &= ~AVATAR_MOTION_OBEY_LOCAL_GRAVITY; - } else { - _motionBehaviors &= ~AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY; - } - if (! (_motionBehaviors & (AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY | AVATAR_MOTION_OBEY_LOCAL_GRAVITY))) { - setGravity(glm::vec3(0.0f)); - } - if (menu->isOptionChecked(MenuOption::StandOnNearbyFloors)) { - _motionBehaviors |= AVATAR_MOTION_STAND_ON_NEARBY_FLOORS; - // standing on floors requires collision with voxels - // TODO: determine what to do with this now that voxels are gone - } else { - _motionBehaviors &= ~AVATAR_MOTION_STAND_ON_NEARBY_FLOORS; - } if (menu->isOptionChecked(MenuOption::KeyboardMotorControl)) { _motionBehaviors |= AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED; } else { @@ -1523,6 +1388,7 @@ void MyAvatar::updateMotionBehavior() { } else { _motionBehaviors &= ~AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED; } + _characterController.setEnabled(menu->isOptionChecked(MenuOption::EnableCharacterController)); _feetTouchFloor = menu->isOptionChecked(MenuOption::ShiftHipsForIdleAnimations); } @@ -1581,10 +1447,6 @@ glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) { return palm->getPosition(); } -void MyAvatar::preSimulation() { - _characterController.setEnabled(_enablePhysics); -} - void MyAvatar::clearDriveKeys() { for (int i = 0; i < MAX_DRIVE_KEYS; ++i) { _driveKeys[i] = 0.0f; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index a37d1c6a30..99c0bdb5df 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -25,7 +25,7 @@ class MyAvatar : public Avatar { Q_PROPERTY(glm::vec3 motorVelocity READ getScriptedMotorVelocity WRITE setScriptedMotorVelocity) Q_PROPERTY(float motorTimescale READ getScriptedMotorTimescale WRITE setScriptedMotorTimescale) Q_PROPERTY(QString motorReferenceFrame READ getScriptedMotorFrame WRITE setScriptedMotorFrame) - Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setLocalGravity) + //TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity) public: MyAvatar(); @@ -44,13 +44,11 @@ public: // setters void setLeanScale(float scale) { _leanScale = scale; } - void setLocalGravity(glm::vec3 gravity); void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; } void setRealWorldFieldOfView(float realWorldFov) { _realWorldFieldOfView.set(realWorldFov); } // getters float getLeanScale() const { return _leanScale; } - glm::vec3 getGravity() const { return _gravity; } Q_INVOKABLE glm::vec3 getDefaultEyePosition() const; bool getShouldRenderLocally() const { return _shouldRender; } float getRealWorldFieldOfView() { return _realWorldFieldOfView.get(); } @@ -148,11 +146,6 @@ public: const RecorderPointer getRecorder() const { return _recorder; } const PlayerPointer getPlayer() const { return _player; } - - void togglePhysicsEnabled() { _enablePhysics = !_enablePhysics; } - bool isPhysicsEnabled() { return _enablePhysics; } - void setPhysicsEnabled(bool enablePhysics) { _enablePhysics = enablePhysics; } - void preSimulation(); public slots: void increaseSize(); @@ -209,7 +202,6 @@ private: int _scriptedMotorFrame; quint32 _motionBehaviors; - bool _enablePhysics; CharacterController _characterController; QWeakPointer _lookAtTargetAvatar; @@ -234,10 +226,8 @@ private: glm::vec3 applyKeyboardMotor(float deltaTime, const glm::vec3& velocity, bool walkingOnFloor); glm::vec3 applyScriptedMotor(float deltaTime, const glm::vec3& velocity); void updatePosition(float deltaTime); - void updatePositionWithPhysics(float deltaTime); void updateCollisionSound(const glm::vec3& penetration, float deltaTime, float frequency); void maybeUpdateBillboard(); - void setGravity(const glm::vec3& gravity); }; #endif // hifi_MyAvatar_h diff --git a/interface/src/devices/TV3DManager.cpp b/interface/src/devices/TV3DManager.cpp index b5f57301f1..f082c6de47 100644 --- a/interface/src/devices/TV3DManager.cpp +++ b/interface/src/devices/TV3DManager.cpp @@ -104,7 +104,6 @@ void TV3DManager::display(Camera& whichCamera) { // We only need to render the overlays to a texture once, then we just render the texture as a quad // PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay() applicationOverlay.renderOverlay(true); - const bool displayOverlays = Menu::getInstance()->isOptionChecked(MenuOption::UserInterface); DependencyManager::get()->prepare(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -135,9 +134,7 @@ void TV3DManager::display(Camera& whichCamera) { eyeCamera.setEyeOffsetPosition(glm::vec3(-_activeEye->modelTranslation,0,0)); Application::getInstance()->displaySide(eyeCamera, false, RenderArgs::MONO); - if (displayOverlays) { - applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov); - } + applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov); _activeEye = NULL; } glPopMatrix(); @@ -166,9 +163,7 @@ void TV3DManager::display(Camera& whichCamera) { eyeCamera.setEyeOffsetPosition(glm::vec3(-_activeEye->modelTranslation,0,0)); Application::getInstance()->displaySide(eyeCamera, false, RenderArgs::MONO); - if (displayOverlays) { - applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov); - } + applicationOverlay.displayOverlayTexture3DTV(whichCamera, _aspect, fov); _activeEye = NULL; } glPopMatrix(); diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 0c5941366f..d9adaf02e6 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -176,18 +176,7 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) { _textureAspectRatio = (float)glCanvas->getDeviceWidth() / (float)glCanvas->getDeviceHeight(); //Handle fading and deactivation/activation of UI - if (Menu::getInstance()->isOptionChecked(MenuOption::UserInterface)) { - _alpha += FADE_SPEED; - if (_alpha > 1.0f) { - _alpha = 1.0f; - } - } else { - _alpha -= FADE_SPEED; - if (_alpha <= 0.0f) { - _alpha = 0.0f; - } - } - + // Render 2D overlay glMatrixMode(GL_PROJECTION); glDisable(GL_DEPTH_TEST); diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 64161e4a31..58b79adcda 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -114,7 +114,7 @@ private: quint64 _lastMouseMove; bool _magnifier; - float _alpha; + float _alpha = 1.0f; float _oculusUIRadius; float _trailingAudioLoudness; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index a2feb98798..ad90c88aaa 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -61,21 +61,13 @@ typedef unsigned long long quint64; const quint32 AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED = 1U << 0; const quint32 AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED = 1U << 1; -const quint32 AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY = 1U << 2; -const quint32 AVATAR_MOTION_OBEY_LOCAL_GRAVITY = 1U << 3; -const quint32 AVATAR_MOTION_STAND_ON_NEARBY_FLOORS = 1U << 4; - const quint32 AVATAR_MOTION_DEFAULTS = AVATAR_MOTION_KEYBOARD_MOTOR_ENABLED | - AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED | - AVATAR_MOTION_STAND_ON_NEARBY_FLOORS; + AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED; // these bits will be expanded as features are exposed const quint32 AVATAR_MOTION_SCRIPTABLE_BITS = - AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED | - AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY | - AVATAR_MOTION_OBEY_LOCAL_GRAVITY | - AVATAR_MOTION_STAND_ON_NEARBY_FLOORS; + AVATAR_MOTION_SCRIPTED_MOTOR_ENABLED; // Bitset of state flags - we store the key state, hand state, faceshift, chat circling, and existance of @@ -365,8 +357,8 @@ protected: HeadData* _headData; HandData* _handData; - QUrl _faceModelURL = DEFAULT_HEAD_MODEL_URL; - QUrl _skeletonModelURL = DEFAULT_BODY_MODEL_URL; + QUrl _faceModelURL; // These need to be empty so that on first time setting them they will not short circuit + QUrl _skeletonModelURL; // These need to be empty so that on first time setting them they will not short circuit QVector _attachmentData; QString _displayName; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index a2c217c97d..1cd3bba73a 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -458,41 +458,6 @@ FBXNode parseFBX(QIODevice* device) { return top; } -QVariantHash parseMapping(QIODevice* device) { - QVariantHash properties; - - QByteArray line; - while (!(line = device->readLine()).isEmpty()) { - if ((line = line.trimmed()).startsWith('#')) { - continue; // comment - } - QList sections = line.split('='); - if (sections.size() < 2) { - continue; - } - QByteArray name = sections.at(0).trimmed(); - if (sections.size() == 2) { - properties.insertMulti(name, sections.at(1).trimmed()); - - } else if (sections.size() == 3) { - QVariantHash heading = properties.value(name).toHash(); - heading.insertMulti(sections.at(1).trimmed(), sections.at(2).trimmed()); - properties.insert(name, heading); - - } else if (sections.size() >= 4) { - QVariantHash heading = properties.value(name).toHash(); - QVariantList contents; - for (int i = 2; i < sections.size(); i++) { - contents.append(sections.at(i).trimmed()); - } - heading.insertMulti(sections.at(1).trimmed(), contents); - properties.insert(name, heading); - } - } - - return properties; -} - QVector createVec3Vector(const QVector& doubleVector) { QVector values; for (const double* it = doubleVector.constData(), *end = it + (doubleVector.size() / 3 * 3); it != end; ) { @@ -2473,39 +2438,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, return geometry; } -QVariantHash readMapping(const QByteArray& data) { - QBuffer buffer(const_cast(&data)); - buffer.open(QIODevice::ReadOnly); - return parseMapping(&buffer); -} - -QByteArray writeMapping(const QVariantHash& mapping) { - QBuffer buffer; - buffer.open(QIODevice::WriteOnly); - for (QVariantHash::const_iterator first = mapping.constBegin(); first != mapping.constEnd(); first++) { - QByteArray key = first.key().toUtf8() + " = "; - QVariantHash hashValue = first.value().toHash(); - if (hashValue.isEmpty()) { - buffer.write(key + first.value().toByteArray() + "\n"); - continue; - } - for (QVariantHash::const_iterator second = hashValue.constBegin(); second != hashValue.constEnd(); second++) { - QByteArray extendedKey = key + second.key().toUtf8(); - QVariantList listValue = second.value().toList(); - if (listValue.isEmpty()) { - buffer.write(extendedKey + " = " + second.value().toByteArray() + "\n"); - continue; - } - buffer.write(extendedKey); - for (QVariantList::const_iterator third = listValue.constBegin(); third != listValue.constEnd(); third++) { - buffer.write(" = " + third->toByteArray()); - } - buffer.write("\n"); - } - } - return buffer.data(); -} - FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping, bool loadLightmaps, float lightmapLevel) { QBuffer buffer(const_cast(&model)); buffer.open(QIODevice::ReadOnly); diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index ecce607575..6cb6b19c05 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -261,12 +261,6 @@ public: Q_DECLARE_METATYPE(FBXGeometry) -/// Reads an FST mapping from the supplied data. -QVariantHash readMapping(const QByteArray& data); - -/// Writes an FST mapping to a byte array. -QByteArray writeMapping(const QVariantHash& mapping); - /// Reads FBX geometry from the supplied model and mapping data. /// \exception QString if an error occurs in parsing FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping, bool loadLightmaps = true, float lightmapLevel = 1.0f); diff --git a/libraries/fbx/src/FSTReader.cpp b/libraries/fbx/src/FSTReader.cpp new file mode 100644 index 0000000000..f1ffe11996 --- /dev/null +++ b/libraries/fbx/src/FSTReader.cpp @@ -0,0 +1,99 @@ +// +// FSTReader.cpp +// +// +// Created by Clement on 3/26/15. +// Copyright 2015 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 + +#include "FSTReader.h" + +QVariantHash parseMapping(QIODevice* device) { + QVariantHash properties; + + QByteArray line; + while (!(line = device->readLine()).isEmpty()) { + if ((line = line.trimmed()).startsWith('#')) { + continue; // comment + } + QList sections = line.split('='); + if (sections.size() < 2) { + continue; + } + QByteArray name = sections.at(0).trimmed(); + if (sections.size() == 2) { + properties.insertMulti(name, sections.at(1).trimmed()); + + } else if (sections.size() == 3) { + QVariantHash heading = properties.value(name).toHash(); + heading.insertMulti(sections.at(1).trimmed(), sections.at(2).trimmed()); + properties.insert(name, heading); + + } else if (sections.size() >= 4) { + QVariantHash heading = properties.value(name).toHash(); + QVariantList contents; + for (int i = 2; i < sections.size(); i++) { + contents.append(sections.at(i).trimmed()); + } + heading.insertMulti(sections.at(1).trimmed(), contents); + properties.insert(name, heading); + } + } + + return properties; +} + +QVariantHash readMapping(const QByteArray& data) { + QBuffer buffer(const_cast(&data)); + buffer.open(QIODevice::ReadOnly); + return parseMapping(&buffer); +} + +void writeVariant(QBuffer& buffer, QVariantHash::const_iterator& it) { + QByteArray key = it.key().toUtf8() + " = "; + QVariantHash hashValue = it.value().toHash(); + if (hashValue.isEmpty()) { + buffer.write(key + it.value().toByteArray() + "\n"); + return; + } + for (QVariantHash::const_iterator second = hashValue.constBegin(); second != hashValue.constEnd(); second++) { + QByteArray extendedKey = key + second.key().toUtf8(); + QVariantList listValue = second.value().toList(); + if (listValue.isEmpty()) { + buffer.write(extendedKey + " = " + second.value().toByteArray() + "\n"); + continue; + } + buffer.write(extendedKey); + for (QVariantList::const_iterator third = listValue.constBegin(); third != listValue.constEnd(); third++) { + buffer.write(" = " + third->toByteArray()); + } + buffer.write("\n"); + } +} + +QByteArray writeMapping(const QVariantHash& mapping) { + static const QStringList PREFERED_ORDER = QStringList() << NAME_FIELD << SCALE_FIELD << FILENAME_FIELD + << TEXDIR_FIELD << JOINT_FIELD << FREE_JOINT_FIELD + << BLENDSHAPE_FIELD << JOINT_INDEX_FIELD; + QBuffer buffer; + buffer.open(QIODevice::WriteOnly); + + for (auto key : PREFERED_ORDER) { + auto it = mapping.find(key); + if (it != mapping.constEnd()) { + writeVariant(buffer, it); + } + } + + for (auto it = mapping.constBegin(); it != mapping.constEnd(); it++) { + if (!PREFERED_ORDER.contains(it.key())) { + writeVariant(buffer, it); + } + } + return buffer.data(); +} \ No newline at end of file diff --git a/libraries/fbx/src/FSTReader.h b/libraries/fbx/src/FSTReader.h new file mode 100644 index 0000000000..59559dea74 --- /dev/null +++ b/libraries/fbx/src/FSTReader.h @@ -0,0 +1,36 @@ +// +// FSTReader.h +// +// +// Created by Clement on 3/26/15. +// Copyright 2015 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_FSTReader_h +#define hifi_FSTReader_h + +#include + +static const QString NAME_FIELD = "name"; +static const QString FILENAME_FIELD = "filename"; +static const QString TEXDIR_FIELD = "texdir"; +static const QString LOD_FIELD = "lod"; +static const QString JOINT_INDEX_FIELD = "jointIndex"; +static const QString SCALE_FIELD = "scale"; +static const QString TRANSLATION_X_FIELD = "tx"; +static const QString TRANSLATION_Y_FIELD = "ty"; +static const QString TRANSLATION_Z_FIELD = "tz"; +static const QString JOINT_FIELD = "joint"; +static const QString FREE_JOINT_FIELD = "freeJoint"; +static const QString BLENDSHAPE_FIELD = "bs"; + +/// Reads an FST mapping from the supplied data. +QVariantHash readMapping(const QByteArray& data); + +/// Writes an FST mapping to a byte array. +QByteArray writeMapping(const QVariantHash& mapping); + +#endif // hifi_FSTReader_h \ No newline at end of file diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 5858c0926d..46c6418c63 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -282,7 +282,7 @@ void AccountManager::processReply() { } else { passErrorToCallback(requestReply); } - delete requestReply; + requestReply->deleteLater(); } void AccountManager::passSuccessToCallback(QNetworkReply* requestReply) { diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index e84df7d644..09c6b5599f 100644 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -40,6 +40,24 @@ static btVector3 getNormalizedVector(const btVector3& v) { return n; } +class btKinematicClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback { +public: + btKinematicClosestNotMeRayResultCallback (btCollisionObject* me) : + btCollisionWorld::ClosestRayResultCallback(btVector3(0.0f, 0.0f, 0.0f), btVector3(0.0f, 0.0f, 0.0f)) { + _me = me; + } + + virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace) { + if (rayResult.m_collisionObject == _me) { + return 1.0f; + } + return ClosestRayResultCallback::addSingleResult (rayResult, normalInWorldSpace); + } +protected: + btCollisionObject* _me; +}; + + class btKinematicClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback { public: btKinematicClosestNotMeConvexResultCallback(btCollisionObject* me, const btVector3& up, btScalar minSlopeDot) @@ -199,9 +217,7 @@ CharacterController::CharacterController(AvatarData* avatarData) { assert(avatarData); _avatarData = avatarData; - // cache the "PhysicsEnabled" state of _avatarData _enabled = false; - _ghostObject = NULL; _convexShape = NULL; @@ -210,11 +226,13 @@ CharacterController::CharacterController(AvatarData* avatarData) { _velocityTimeInterval = 0.0f; _verticalVelocity = 0.0f; _verticalOffset = 0.0f; - _gravity = 9.8f; + _gravity = 5.0f; // slower than Earth's _maxFallSpeed = 55.0f; // Terminal velocity of a sky diver in m/s. - _jumpSpeed = 7.0f; - _wasOnGround = false; - _wasJumping = false; + _jumpSpeed = 5.0f; + _isOnGround = false; + _isJumping = false; + _isHovering = true; + _jumpToHoverStart = 0; setMaxSlope(btRadians(45.0f)); _lastStepUp = 0.0f; _pendingFlags = 0; @@ -325,6 +343,26 @@ bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorl return penetration; } +void CharacterController::scanDown(btCollisionWorld* world) { + // we test with downward raycast and if we don't find floor close enough then turn on "hover" + btKinematicClosestNotMeRayResultCallback callback(_ghostObject); + callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; + callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; + + btVector3 up = quatRotate(_currentRotation, LOCAL_UP_AXIS); + btVector3 start = _currentPosition; + const btScalar MAX_SCAN_HEIGHT = 20.0f + _halfHeight + _radius; // closest possible floor for disabling hover + const btScalar MIN_HOVER_HEIGHT = 3.0f + _halfHeight + _radius; // distance to floor for enabling hover + btVector3 end = start - MAX_SCAN_HEIGHT * up; + + world->rayTest(start, end, callback); + if (!callback.hasHit()) { + _isHovering = true; + } else if (_isHovering && callback.m_closestHitFraction * MAX_SCAN_HEIGHT < MIN_HOVER_HEIGHT) { + _isHovering = false; + } +} + void CharacterController::stepUp(btCollisionWorld* world) { // phase 1: up @@ -334,7 +372,7 @@ void CharacterController::stepUp(btCollisionWorld* world) { btVector3 up = quatRotate(_currentRotation, LOCAL_UP_AXIS); start.setOrigin(_currentPosition + up * (_convexShape->getMargin() + _addedMargin)); - _targetPosition = _currentPosition + up * _stepHeight; + _targetPosition = _currentPosition + up * _stepUpHeight; end.setIdentity(); end.setOrigin(_targetPosition); @@ -352,15 +390,15 @@ void CharacterController::stepUp(btCollisionWorld* world) { // Only modify the position if the hit was a slope and not a wall or ceiling. if (callback.m_hitNormalWorld.dot(up) > 0.0f) { - _lastStepUp = _stepHeight * callback.m_closestHitFraction; + _lastStepUp = _stepUpHeight * callback.m_closestHitFraction; _currentPosition.setInterpolate3(_currentPosition, _targetPosition, callback.m_closestHitFraction); } else { - _lastStepUp = _stepHeight; + _lastStepUp = _stepUpHeight; _currentPosition = _targetPosition; } } else { _currentPosition = _targetPosition; - _lastStepUp = _stepHeight; + _lastStepUp = _stepUpHeight; } } @@ -411,12 +449,12 @@ void CharacterController::stepForward(btCollisionWorld* collisionWorld, const bt btScalar margin = _convexShape->getMargin(); _convexShape->setMargin(margin + _addedMargin); - const btScalar MIN_STEP_DISTANCE = 0.0001f; + const btScalar MIN_STEP_DISTANCE_SQUARED = 1.0e-6f; btVector3 step = _targetPosition - _currentPosition; btScalar stepLength2 = step.length2(); int maxIter = 10; - while (stepLength2 > MIN_STEP_DISTANCE && maxIter-- > 0) { + while (stepLength2 > MIN_STEP_DISTANCE_SQUARED && maxIter-- > 0) { start.setOrigin(_currentPosition); end.setOrigin(_targetPosition); @@ -477,13 +515,16 @@ void CharacterController::stepDown(btCollisionWorld* collisionWorld, btScalar dt end.setOrigin(_targetPosition); _ghostObject->convexSweepTest(_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); + _isOnGround = false; if (callback.hasHit()) { _currentPosition += callback.m_closestHitFraction * step; _verticalVelocity = 0.0f; _verticalOffset = 0.0f; - _wasJumping = false; - } else if (!_wasJumping) { + _isJumping = false; + _isOnGround = true; + } else if (!_isJumping) { // sweep again for floor within downStep threshold + step = -_stepDownHeight * up; StepDownConvexResultCallback callback2 (_ghostObject, up, _currentPosition, step, @@ -495,8 +536,6 @@ void CharacterController::stepDown(btCollisionWorld* collisionWorld, btScalar dt callback2.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; _currentPosition = _targetPosition; - btVector3 oldPosition = _currentPosition; - step = (- _stepHeight) * up; _targetPosition = _currentPosition + step; start.setOrigin(_currentPosition); @@ -507,10 +546,10 @@ void CharacterController::stepDown(btCollisionWorld* collisionWorld, btScalar dt _currentPosition += callback2.m_closestHitFraction * step; _verticalVelocity = 0.0f; _verticalOffset = 0.0f; - _wasJumping = false; + _isJumping = false; + _isOnGround = true; } else { - // nothing to step down on, so remove the stepUp effect - _currentPosition = oldPosition - _lastStepUp * up; + // nothing to step down on _lastStepUp = 0.0f; } } else { @@ -534,8 +573,8 @@ void CharacterController::setVelocityForTimeInterval(const btVector3& velocity, void CharacterController::reset(btCollisionWorld* collisionWorld) { _verticalVelocity = 0.0; _verticalOffset = 0.0; - _wasOnGround = false; - _wasJumping = false; + _isOnGround = false; + _isJumping = false; _walkDirection.setValue(0,0,0); _velocityTimeInterval = 0.0; @@ -581,14 +620,17 @@ void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar return; // no motion } - _wasOnGround = onGround(); - // Update fall velocity. - _verticalVelocity -= _gravity * dt; - if (_verticalVelocity > _jumpSpeed) { - _verticalVelocity = _jumpSpeed; - } else if (_verticalVelocity < -_maxFallSpeed) { - _verticalVelocity = -_maxFallSpeed; + if (_isHovering) { + const btScalar HOVER_RELAXATION_TIMESCALE = 1.0f; + _verticalVelocity *= (1.0f - dt / HOVER_RELAXATION_TIMESCALE); + } else { + _verticalVelocity -= _gravity * dt; + if (_verticalVelocity > _jumpSpeed) { + _verticalVelocity = _jumpSpeed; + } else if (_verticalVelocity < -_maxFallSpeed) { + _verticalVelocity = -_maxFallSpeed; + } } _verticalOffset = _verticalVelocity * dt; @@ -600,11 +642,14 @@ void CharacterController::playerStep(btCollisionWorld* collisionWorld, btScalar // (2) step the character forward // (3) step the character down looking for new ledges, the original floor, or a floor one step below where we started + scanDown(collisionWorld); + stepUp(collisionWorld); // compute substep and decrement total interval btScalar dtMoving = (dt < _velocityTimeInterval) ? dt : _velocityTimeInterval; _velocityTimeInterval -= dt; + _stepDt += dt; // stepForward substep btVector3 move = _walkDirection * dtMoving; @@ -629,11 +674,25 @@ void CharacterController::setMaxJumpHeight(btScalar maxJumpHeight) { } bool CharacterController::canJump() const { - return onGround(); + return _isOnGround; } void CharacterController::jump() { _pendingFlags |= PENDING_FLAG_JUMP; + + // check for case where user is holding down "jump" key... + // we'll eventually tansition to "hover" + if (!_isHovering) { + if (!_isJumping) { + _jumpToHoverStart = usecTimestampNow(); + } else { + quint64 now = usecTimestampNow(); + const quint64 JUMP_TO_HOVER_PERIOD = USECS_PER_SECOND; + if (now - _jumpToHoverStart < JUMP_TO_HOVER_PERIOD) { + _isHovering = true; + } + } + } } void CharacterController::setGravity(btScalar gravity) { @@ -654,7 +713,7 @@ btScalar CharacterController::getMaxSlope() const { } bool CharacterController::onGround() const { - return _enabled && _verticalVelocity == 0.0f && _verticalOffset == 0.0f; + return _isOnGround; } void CharacterController::debugDraw(btIDebugDraw* debugDrawer) { @@ -684,10 +743,12 @@ void CharacterController::setLocalBoundingBox(const glm::vec3& corner, const glm if (radiusDelta < FLT_EPSILON && heightDelta < FLT_EPSILON) { // shape hasn't changed --> nothing to do } else { - // we always need to: REMOVE when UPDATE_SHAPE, to avoid deleting shapes out from under the PhysicsEngine - _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION - | PENDING_FLAG_UPDATE_SHAPE; - // but only need to ADD back when we happen to be enabled + if (_dynamicsWorld) { + // must REMOVE from world prior to shape update + _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION; + } + _pendingFlags |= PENDING_FLAG_UPDATE_SHAPE; + // only need to ADD back when we happen to be enabled if (_enabled) { _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION; } @@ -711,11 +772,13 @@ void CharacterController::setEnabled(bool 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; + _isHovering = true; } else { - // Always set REMOVE bit when going disabled, and we always clear the ADD bit just in case - // it was previously set by something else (e.g. an UPDATE_SHAPE event). - _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION; + if (_dynamicsWorld) { + _pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION; + } _pendingFlags &= ~ PENDING_FLAG_ADD_TO_SIMULATION; + _isOnGround = false; } _enabled = enabled; } @@ -729,17 +792,23 @@ void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { } _dynamicsWorld = world; if (_dynamicsWorld) { - _pendingFlags &= ~ (PENDING_FLAG_ADD_TO_SIMULATION | PENDING_FLAG_JUMP); + _pendingFlags &= ~ PENDING_FLAG_JUMP; _dynamicsWorld->addCollisionObject(getGhostObject(), btBroadphaseProxy::CharacterFilter, btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter); _dynamicsWorld->addAction(this); reset(_dynamicsWorld); + } + } + if (_dynamicsWorld) { + if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) { + // shouldn't fall in here, but if we do make sure both ADD and REMOVE bits are still set + _pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION | PENDING_FLAG_REMOVE_FROM_SIMULATION; } else { - _pendingFlags &= ~ PENDING_FLAG_REMOVE_FROM_SIMULATION; + _pendingFlags &= ~PENDING_FLAG_ADD_TO_SIMULATION; } } else { - _pendingFlags &= ~ (PENDING_FLAG_REMOVE_FROM_SIMULATION | PENDING_FLAG_ADD_TO_SIMULATION); + _pendingFlags &= ~ PENDING_FLAG_REMOVE_FROM_SIMULATION; } } @@ -772,10 +841,8 @@ void CharacterController::updateShapeIfNecessary() { _ghostObject->setWorldTransform(btTransform(glmToBullet(_avatarData->getOrientation()), glmToBullet(_avatarData->getPosition()))); // stepHeight affects the heights of ledges that the character can ascend - // however the actual ledge height is some function of _stepHeight - // due to character shape and this CharacterController algorithm - // (the function is approximately 2*_stepHeight) - _stepHeight = 0.1f * (_radius + _halfHeight) + 0.1f; + _stepUpHeight = _radius + 0.25f * _halfHeight + 0.1f; + _stepDownHeight = _radius; // create new shape _convexShape = new btCapsuleShape(_radius, 2.0f * _halfHeight); @@ -799,9 +866,12 @@ void CharacterController::preSimulation(btScalar timeStep) { _pendingFlags &= ~ PENDING_FLAG_JUMP; if (canJump()) { _verticalVelocity = _jumpSpeed; - _wasJumping = true; + _isJumping = true; } } + // remember last position so we can throttle the total motion from the next step + _lastPosition = position; + _stepDt = 0.0f; } } @@ -809,9 +879,24 @@ void CharacterController::postSimulation() { if (_enabled) { const btTransform& avatarTransform = _ghostObject->getWorldTransform(); glm::quat rotation = bulletToGLM(avatarTransform.getRotation()); - glm::vec3 offset = rotation * _shapeLocalOffset; + glm::vec3 position = bulletToGLM(avatarTransform.getOrigin()); + + // cap the velocity of the step so that the character doesn't POP! so hard on steps + glm::vec3 finalStep = position - _lastPosition; + btVector3 finalVelocity = _walkDirection; + btVector3 up = quatRotate(_currentRotation, LOCAL_UP_AXIS); + finalVelocity += _verticalVelocity * up; + const btScalar MAX_RESOLUTION_SPEED = 5.0f; // m/sec + btScalar maxStepLength = glm::max(MAX_RESOLUTION_SPEED, 2.0f * finalVelocity.length()) * _stepDt; + btScalar stepLength = glm::length(finalStep); + if (stepLength > maxStepLength) { + position = _lastPosition + (maxStepLength / stepLength) * finalStep; + // NOTE: we don't need to move ghostObject to throttled position unless + // we want to support do async ray-traces/collision-queries against character + } + _avatarData->setOrientation(rotation); - _avatarData->setPosition(bulletToGLM(avatarTransform.getOrigin()) - offset); + _avatarData->setPosition(position - rotation * _shapeLocalOffset); } } diff --git a/libraries/physics/src/CharacterController.h b/libraries/physics/src/CharacterController.h index 323529b1cd..eeaa5836dd 100644 --- a/libraries/physics/src/CharacterController.h +++ b/libraries/physics/src/CharacterController.h @@ -59,7 +59,8 @@ protected: btScalar _maxSlopeCosine; // Cosine equivalent of _maxSlopeRadians (calculated once when set, for optimization) btScalar _gravity; - btScalar _stepHeight; // height of stepUp prior to stepForward + btScalar _stepUpHeight; // height of stepUp prior to stepForward + btScalar _stepDownHeight; // height of stepDown btScalar _addedMargin;//@todo: remove this and fix the code @@ -71,6 +72,7 @@ protected: btVector3 _currentPosition; btQuaternion _currentRotation; btVector3 _targetPosition; + glm::vec3 _lastPosition; btScalar _lastStepUp; ///keep track of the contact manifolds @@ -80,9 +82,12 @@ protected: btVector3 _floorNormal; // points from object to character bool _enabled; - bool _wasOnGround; - bool _wasJumping; + bool _isOnGround; + bool _isJumping; + bool _isHovering; + quint64 _jumpToHoverStart; btScalar _velocityTimeInterval; + btScalar _stepDt; uint32_t _pendingFlags; glm::vec3 _shapeLocalOffset; @@ -95,6 +100,7 @@ protected: btVector3 perpindicularComponent(const btVector3& direction, const btVector3& normal); bool recoverFromPenetration(btCollisionWorld* collisionWorld); + void scanDown(btCollisionWorld* collisionWorld); void stepUp(btCollisionWorld* collisionWorld); void updateTargetPositionBasedOnCollision(const btVector3& hit_normal, btScalar tangentMag = btScalar(0.0), btScalar normalMag = btScalar(1.0)); void stepForward(btCollisionWorld* collisionWorld, const btVector3& walkMove); @@ -161,6 +167,7 @@ public: bool needsRemoval() const; bool needsAddition() const; void setEnabled(bool enabled); + bool isEnabled() const { return _enabled; } void setDynamicsWorld(btDynamicsWorld* world); void setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale); diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index c18dfa4d5f..f8fc4633cc 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include "TextureCache.h" diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 716af1d188..831db73a0a 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -360,10 +360,6 @@ void ScriptEngine::init() { globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE))); globalObject().setProperty("COLLISION_GROUP_ENVIRONMENT", newVariant(QVariant(COLLISION_GROUP_ENVIRONMENT))); globalObject().setProperty("COLLISION_GROUP_AVATARS", newVariant(QVariant(COLLISION_GROUP_AVATARS))); - - globalObject().setProperty("AVATAR_MOTION_OBEY_LOCAL_GRAVITY", newVariant(QVariant(AVATAR_MOTION_OBEY_LOCAL_GRAVITY))); - globalObject().setProperty("AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY", newVariant(QVariant(AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY))); - } QScriptValue ScriptEngine::registerGlobalObject(const QString& name, QObject* object) { diff --git a/libraries/script-engine/src/XMLHttpRequestClass.cpp b/libraries/script-engine/src/XMLHttpRequestClass.cpp index 8755527860..3054472a3c 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.cpp +++ b/libraries/script-engine/src/XMLHttpRequestClass.cpp @@ -22,6 +22,8 @@ #include "XMLHttpRequestClass.h" #include "ScriptEngine.h" +const QString METAVERSE_API_URL = "https://metaverse.highfidelity.com/api/"; + Q_DECLARE_METATYPE(QByteArray*) XMLHttpRequestClass::XMLHttpRequestClass(QScriptEngine* engine) : @@ -207,7 +209,7 @@ void XMLHttpRequestClass::open(const QString& method, const QString& url, bool a notImplemented(); } } else { - if (url.toLower().left(33) == "https://metaverse.highfidelity.com/api/") { + if (url.toLower().left(METAVERSE_API_URL.length()) == METAVERSE_API_URL) { AccountManager& accountManager = AccountManager::getInstance(); if (accountManager.hasValidAccessToken()) {