mirror of
https://github.com/AleziaKurdis/overte.git
synced 2025-04-09 11:12:40 +02:00
Merge pull request #16144 from luiscuenca/addLookAtThirdCamera
DEV-391: Keyboard Input Controls Camera Not Avatar Orientation
This commit is contained in:
commit
bd488f0196
12 changed files with 4894 additions and 4343 deletions
BIN
interface/resources/avatar/animations/idle_aimoffsets.fbx
Normal file
BIN
interface/resources/avatar/animations/idle_aimoffsets.fbx
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load diff
|
@ -3,8 +3,8 @@
|
|||
"channels": [
|
||||
{ "from": "Keyboard.A", "when": ["Keyboard.RightMouseButton", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" },
|
||||
{ "from": "Keyboard.D", "when": ["Keyboard.RightMouseButton", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" },
|
||||
{ "from": "Keyboard.E", "when": "!Keyboard.Control", "to": "Actions.LATERAL_RIGHT" },
|
||||
{ "from": "Keyboard.Q", "when": "!Keyboard.Control", "to": "Actions.LATERAL_LEFT" },
|
||||
{ "from": "Keyboard.E", "when": ["!Application.CameraSelfie", "!Application.CameraLookAt", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" },
|
||||
{ "from": "Keyboard.Q", "when": ["!Application.CameraSelfie"," !Application.CameraLookAt", "!Keyboard.Control"], "to": "Actions.LATERAL_LEFT" },
|
||||
{ "from": "Keyboard.T", "when": "!Keyboard.Control", "to": "Actions.TogglePushToTalk" },
|
||||
|
||||
{ "comment" : "Mouse turn need to be small continuous increments",
|
||||
|
@ -39,7 +39,6 @@
|
|||
]
|
||||
},
|
||||
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.Left" ],
|
||||
["Keyboard.Right"]
|
||||
|
@ -85,6 +84,24 @@
|
|||
"when": ["Application.CameraThirdPerson", "!Keyboard.Shift"],
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.Left"],
|
||||
["Keyboard.Right"]
|
||||
]
|
||||
},
|
||||
"when": ["Application.CameraLookAt", "!Keyboard.Shift"],
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.Left"],
|
||||
["Keyboard.Right"]
|
||||
]
|
||||
},
|
||||
"when": ["Application.CameraSelfie", "!Keyboard.Shift"],
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.A"],
|
||||
|
@ -104,6 +121,24 @@
|
|||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.Q"],
|
||||
["Keyboard.E"]
|
||||
]
|
||||
},
|
||||
"when": ["Application.CameraLookAt", "!Keyboard.Control"],
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.E"],
|
||||
["Keyboard.Q"]
|
||||
]
|
||||
},
|
||||
"when": ["Application.CameraSelfie", "!Keyboard.Control"],
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.TouchpadLeft"],
|
||||
["Keyboard.TouchpadRight"]
|
||||
|
@ -122,6 +157,24 @@
|
|||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.TouchpadLeft"],
|
||||
["Keyboard.TouchpadRight"]
|
||||
]
|
||||
},
|
||||
"when": "Application.CameraLookAt",
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : [
|
||||
["Keyboard.TouchpadLeft"],
|
||||
["Keyboard.TouchpadRight"]
|
||||
]
|
||||
},
|
||||
"when": "Application.CameraSelfie",
|
||||
"to": "Actions.Yaw"
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] },
|
||||
"when": "Keyboard.RightMouseButton",
|
||||
"to": "Actions.DeltaYaw",
|
||||
|
@ -132,7 +185,7 @@
|
|||
},
|
||||
|
||||
{ "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] },
|
||||
"when": "Keyboard.RightMouseButton",
|
||||
"when": ["!Application.CameraSelfie", "!Application.CameraLookAt", "Keyboard.RightMouseButton"],
|
||||
"to": "Actions.DeltaPitch",
|
||||
"filters":
|
||||
[
|
||||
|
@ -140,16 +193,44 @@
|
|||
]
|
||||
},
|
||||
|
||||
{ "from": "Keyboard.W", "when": "!Keyboard.Control", "to": "Actions.LONGITUDINAL_FORWARD" },
|
||||
{ "from": "Keyboard.S", "when": "!Keyboard.Control", "to": "Actions.LONGITUDINAL_BACKWARD" },
|
||||
{ "from": { "makeAxis" : ["Keyboard.MouseMoveUp", "Keyboard.MouseMoveDown"] },
|
||||
"when": ["Application.CameraLookAt", "Keyboard.RightMouseButton"],
|
||||
"to": "Actions.DeltaPitch",
|
||||
"filters":
|
||||
[
|
||||
{ "type": "scale", "scale": 0.3 }
|
||||
]
|
||||
},
|
||||
|
||||
{ "from": { "makeAxis" : ["Keyboard.MouseMoveDown", "Keyboard.MouseMoveUp"] },
|
||||
"when": ["Application.CameraSelfie", "Keyboard.RightMouseButton"],
|
||||
"to": "Actions.DeltaPitch",
|
||||
"filters":
|
||||
[
|
||||
{ "type": "scale", "scale": 0.3 }
|
||||
]
|
||||
},
|
||||
|
||||
{ "from": "Keyboard.W", "when": ["!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LONGITUDINAL_FORWARD" },
|
||||
{ "from": "Keyboard.S", "when": ["!Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LONGITUDINAL_BACKWARD" },
|
||||
{ "from": "Keyboard.S", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LONGITUDINAL_FORWARD" },
|
||||
{ "from": "Keyboard.W", "when": ["Application.CameraSelfie", "!Keyboard.Control"], "to": "Actions.LONGITUDINAL_BACKWARD" },
|
||||
{ "from": "Keyboard.A", "when": "Application.CameraLookAt", "to": "Actions.LATERAL_LEFT" },
|
||||
{ "from": "Keyboard.D", "when": "Application.CameraLookAt", "to": "Actions.LATERAL_RIGHT" },
|
||||
{ "from": "Keyboard.A", "when": "Application.CameraSelfie", "to": "Actions.LATERAL_RIGHT" },
|
||||
{ "from": "Keyboard.D", "when": "Application.CameraSelfie", "to": "Actions.LATERAL_LEFT" },
|
||||
{ "from": "Keyboard.Shift", "when": ["!Keyboard.Left", "!Keyboard.Right"], "to": "Actions.SPRINT" },
|
||||
{ "from": "Keyboard.C", "when": "!Keyboard.Control", "to": "Actions.VERTICAL_DOWN" },
|
||||
{ "from": "Keyboard.Left", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" },
|
||||
{ "from": "Keyboard.Right", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" },
|
||||
{ "from": "Keyboard.Up", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_FORWARD" },
|
||||
{ "from": "Keyboard.Up", "when": "Application.CameraThirdPerson", "to": "Actions.LONGITUDINAL_FORWARD" },
|
||||
{ "from": "Keyboard.Up", "when": "Application.CameraLookAt", "to": "Actions.LONGITUDINAL_FORWARD" },
|
||||
{ "from": "Keyboard.Up", "when": "Application.CameraSelfie", "to": "Actions.LONGITUDINAL_BACKWARD" },
|
||||
{ "from": "Keyboard.Down", "when": "Application.CameraFirstPerson", "to": "Actions.LONGITUDINAL_BACKWARD" },
|
||||
{ "from": "Keyboard.Down", "when": "Application.CameraThirdPerson", "to": "Actions.LONGITUDINAL_BACKWARD" },
|
||||
{ "from": "Keyboard.Down", "when": "Application.CameraLookAt", "to": "Actions.LONGITUDINAL_BACKWARD" },
|
||||
{ "from": "Keyboard.Down", "when": "Application.CameraSelfie", "to": "Actions.LONGITUDINAL_FORWARD" },
|
||||
|
||||
{ "from": "Keyboard.PgDown", "to": "Actions.VERTICAL_DOWN" },
|
||||
{ "from": "Keyboard.PgUp", "to": "Actions.VERTICAL_UP" },
|
||||
|
|
|
@ -709,6 +709,8 @@ static const QString STATE_CAMERA_FIRST_PERSON = "CameraFirstPerson";
|
|||
static const QString STATE_CAMERA_THIRD_PERSON = "CameraThirdPerson";
|
||||
static const QString STATE_CAMERA_ENTITY = "CameraEntity";
|
||||
static const QString STATE_CAMERA_INDEPENDENT = "CameraIndependent";
|
||||
static const QString STATE_CAMERA_LOOK_AT = "CameraLookAt";
|
||||
static const QString STATE_CAMERA_SELFIE = "CameraSelfie";
|
||||
static const QString STATE_SNAP_TURN = "SnapTurn";
|
||||
static const QString STATE_ADVANCED_MOVEMENT_CONTROLS = "AdvancedMovement";
|
||||
static const QString STATE_GROUNDED = "Grounded";
|
||||
|
@ -924,7 +926,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
DependencyManager::set<AudioInjectorManager>();
|
||||
DependencyManager::set<MessagesClient>();
|
||||
controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR,
|
||||
STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_THIRD_PERSON, STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT,
|
||||
STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_THIRD_PERSON, STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT, STATE_CAMERA_LOOK_AT, STATE_CAMERA_SELFIE,
|
||||
STATE_SNAP_TURN, STATE_ADVANCED_MOVEMENT_CONTROLS, STATE_GROUNDED, STATE_NAV_FOCUSED,
|
||||
STATE_PLATFORM_WINDOWS, STATE_PLATFORM_MAC, STATE_PLATFORM_ANDROID, STATE_LEFT_HAND_DOMINANT, STATE_RIGHT_HAND_DOMINANT, STATE_STRAFE_ENABLED } });
|
||||
DependencyManager::set<UserInputMapper>();
|
||||
|
@ -1874,6 +1876,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
_applicationStateDevice->setInputVariant(STATE_CAMERA_THIRD_PERSON, []() -> float {
|
||||
return qApp->getCamera().getMode() == CAMERA_MODE_THIRD_PERSON ? 1 : 0;
|
||||
});
|
||||
_applicationStateDevice->setInputVariant(STATE_CAMERA_LOOK_AT, []() -> float {
|
||||
return qApp->getCamera().getMode() == CAMERA_MODE_LOOK_AT ? 1 : 0;
|
||||
});
|
||||
_applicationStateDevice->setInputVariant(STATE_CAMERA_SELFIE, []() -> float {
|
||||
return qApp->getCamera().getMode() == CAMERA_MODE_SELFIE ? 1 : 0;
|
||||
});
|
||||
_applicationStateDevice->setInputVariant(STATE_CAMERA_ENTITY, []() -> float {
|
||||
return qApp->getCamera().getMode() == CAMERA_MODE_ENTITY ? 1 : 0;
|
||||
});
|
||||
|
@ -3586,7 +3594,8 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) {
|
|||
// Always use the default eye position, not the actual head eye position.
|
||||
// Using the latter will cause the camera to wobble with idle animations,
|
||||
// or with changes from the face tracker
|
||||
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
|
||||
CameraMode mode = _myCamera.getMode();
|
||||
if (mode == CAMERA_MODE_FIRST_PERSON) {
|
||||
_thirdPersonHMDCameraBoomValid= false;
|
||||
if (isHMDMode()) {
|
||||
mat4 camMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix();
|
||||
|
@ -3597,10 +3606,8 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) {
|
|||
_myCamera.setPosition(myAvatar->getDefaultEyePosition());
|
||||
_myCamera.setOrientation(myAvatar->getMyHead()->getHeadOrientation());
|
||||
}
|
||||
}
|
||||
else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
|
||||
} else if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) {
|
||||
if (isHMDMode()) {
|
||||
|
||||
if (!_thirdPersonHMDCameraBoomValid) {
|
||||
const glm::vec3 CAMERA_OFFSET = glm::vec3(0.0f, 0.0f, 0.7f);
|
||||
_thirdPersonHMDCameraBoom = cancelOutRollAndPitch(myAvatar->getHMDSensorOrientation()) * CAMERA_OFFSET;
|
||||
|
@ -3615,22 +3622,28 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) {
|
|||
|
||||
_myCamera.setOrientation(glm::normalize(glmExtractRotation(worldCameraMat)));
|
||||
_myCamera.setPosition(extractTranslation(worldCameraMat));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
_thirdPersonHMDCameraBoomValid = false;
|
||||
|
||||
_myCamera.setOrientation(myAvatar->getHead()->getOrientation());
|
||||
if (isOptionChecked(MenuOption::CenterPlayerInView)) {
|
||||
if (mode == CAMERA_MODE_THIRD_PERSON) {
|
||||
_myCamera.setOrientation(myAvatar->getHead()->getOrientation());
|
||||
if (isOptionChecked(MenuOption::CenterPlayerInView)) {
|
||||
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
|
||||
+ _myCamera.getOrientation() * boomOffset);
|
||||
} else {
|
||||
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
|
||||
+ myAvatar->getWorldOrientation() * boomOffset);
|
||||
}
|
||||
} else {
|
||||
glm::quat lookAtRotation = myAvatar->getLookAtRotation();
|
||||
if (mode == CAMERA_MODE_SELFIE) {
|
||||
lookAtRotation = lookAtRotation * glm::angleAxis(PI, myAvatar->getWorldOrientation() * Vectors::UP);
|
||||
}
|
||||
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
|
||||
+ _myCamera.getOrientation() * boomOffset);
|
||||
}
|
||||
else {
|
||||
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
|
||||
+ myAvatar->getWorldOrientation() * boomOffset);
|
||||
+ lookAtRotation * boomOffset);
|
||||
_myCamera.lookAt(myAvatar->getDefaultEyePosition());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
|
||||
} else if (mode == CAMERA_MODE_MIRROR) {
|
||||
_thirdPersonHMDCameraBoomValid= false;
|
||||
|
||||
if (isHMDMode()) {
|
||||
|
@ -3668,8 +3681,7 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) {
|
|||
glm::vec3(0.0f, 0.0f, -1.0f) * myAvatar->getBoomLength() * _scaleMirror);
|
||||
}
|
||||
renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
|
||||
}
|
||||
else if (_myCamera.getMode() == CAMERA_MODE_ENTITY) {
|
||||
} else if (mode == CAMERA_MODE_ENTITY) {
|
||||
_thirdPersonHMDCameraBoomValid= false;
|
||||
EntityItemPointer cameraEntity = _myCamera.getCameraEntityPointer();
|
||||
if (cameraEntity != nullptr) {
|
||||
|
@ -4387,12 +4399,12 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
}
|
||||
case Qt::Key_2: {
|
||||
Menu* menu = Menu::getInstance();
|
||||
menu->triggerOption(MenuOption::FullscreenMirror);
|
||||
menu->triggerOption(MenuOption::SelfieCamera);
|
||||
break;
|
||||
}
|
||||
case Qt::Key_3: {
|
||||
Menu* menu = Menu::getInstance();
|
||||
menu->triggerOption(MenuOption::ThirdPerson);
|
||||
menu->triggerOption(MenuOption::LookAtCamera);
|
||||
break;
|
||||
}
|
||||
case Qt::Key_4:
|
||||
|
@ -5484,7 +5496,7 @@ void Application::loadSettings() {
|
|||
// dictated that we should be in first person
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, isFirstPerson);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, !isFirstPerson);
|
||||
_myCamera.setMode((isFirstPerson) ? CAMERA_MODE_FIRST_PERSON : CAMERA_MODE_THIRD_PERSON);
|
||||
_myCamera.setMode((isFirstPerson) ? CAMERA_MODE_FIRST_PERSON : CAMERA_MODE_LOOK_AT);
|
||||
cameraMenuChanged();
|
||||
|
||||
auto inputs = pluginManager->getInputPlugins();
|
||||
|
@ -5842,11 +5854,16 @@ void Application::cycleCamera() {
|
|||
} else if (menu->isOptionChecked(MenuOption::FirstPerson)) {
|
||||
|
||||
menu->setIsOptionChecked(MenuOption::FirstPerson, false);
|
||||
menu->setIsOptionChecked(MenuOption::ThirdPerson, true);
|
||||
menu->setIsOptionChecked(MenuOption::LookAtCamera, true);
|
||||
|
||||
} else if (menu->isOptionChecked(MenuOption::ThirdPerson)) {
|
||||
} else if (menu->isOptionChecked(MenuOption::LookAtCamera)) {
|
||||
|
||||
menu->setIsOptionChecked(MenuOption::ThirdPerson, false);
|
||||
menu->setIsOptionChecked(MenuOption::LookAtCamera, false);
|
||||
menu->setIsOptionChecked(MenuOption::SelfieCamera, true);
|
||||
|
||||
} else if (menu->isOptionChecked(MenuOption::SelfieCamera)) {
|
||||
|
||||
menu->setIsOptionChecked(MenuOption::SelfieCamera, false);
|
||||
menu->setIsOptionChecked(MenuOption::FullscreenMirror, true);
|
||||
|
||||
}
|
||||
|
@ -5858,11 +5875,11 @@ void Application::cameraModeChanged() {
|
|||
case CAMERA_MODE_FIRST_PERSON:
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, true);
|
||||
break;
|
||||
case CAMERA_MODE_THIRD_PERSON:
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true);
|
||||
case CAMERA_MODE_LOOK_AT:
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::LookAtCamera, true);
|
||||
break;
|
||||
case CAMERA_MODE_MIRROR:
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FullscreenMirror, true);
|
||||
case CAMERA_MODE_SELFIE:
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::SelfieCamera, true);
|
||||
break;
|
||||
default:
|
||||
// we don't have menu items for the others, so just leave it alone.
|
||||
|
@ -5878,32 +5895,32 @@ void Application::changeViewAsNeeded(float boomLength) {
|
|||
|
||||
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON && boomLengthGreaterThanMinimum) {
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, false);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::LookAtCamera, true);
|
||||
cameraMenuChanged();
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON && !boomLengthGreaterThanMinimum) {
|
||||
} else if (_myCamera.getMode() == CAMERA_MODE_LOOK_AT && !boomLengthGreaterThanMinimum) {
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::FirstPerson, true);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, false);
|
||||
Menu::getInstance()->setIsOptionChecked(MenuOption::LookAtCamera, false);
|
||||
cameraMenuChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::cameraMenuChanged() {
|
||||
auto menu = Menu::getInstance();
|
||||
if (menu->isOptionChecked(MenuOption::FullscreenMirror)) {
|
||||
if (!isHMDMode() && _myCamera.getMode() != CAMERA_MODE_MIRROR) {
|
||||
_mirrorYawOffset = 0.0f;
|
||||
_myCamera.setMode(CAMERA_MODE_MIRROR);
|
||||
getMyAvatar()->reset(false, false, false); // to reset any active MyAvatar::FollowHelpers
|
||||
getMyAvatar()->setBoomLength(MyAvatar::ZOOM_DEFAULT);
|
||||
}
|
||||
} else if (menu->isOptionChecked(MenuOption::FirstPerson)) {
|
||||
if (menu->isOptionChecked(MenuOption::FirstPerson)) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_FIRST_PERSON) {
|
||||
_myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
|
||||
getMyAvatar()->setBoomLength(MyAvatar::ZOOM_MIN);
|
||||
}
|
||||
} else if (menu->isOptionChecked(MenuOption::ThirdPerson)) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_THIRD_PERSON) {
|
||||
_myCamera.setMode(CAMERA_MODE_THIRD_PERSON);
|
||||
} else if (menu->isOptionChecked(MenuOption::LookAtCamera)) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_LOOK_AT) {
|
||||
_myCamera.setMode(CAMERA_MODE_LOOK_AT);
|
||||
if (getMyAvatar()->getBoomLength() == MyAvatar::ZOOM_MIN) {
|
||||
getMyAvatar()->setBoomLength(MyAvatar::ZOOM_DEFAULT);
|
||||
}
|
||||
}
|
||||
} else if (menu->isOptionChecked(MenuOption::SelfieCamera)) {
|
||||
if (_myCamera.getMode() != CAMERA_MODE_SELFIE) {
|
||||
_myCamera.setMode(CAMERA_MODE_SELFIE);
|
||||
if (getMyAvatar()->getBoomLength() == MyAvatar::ZOOM_MIN) {
|
||||
getMyAvatar()->setBoomLength(MyAvatar::ZOOM_DEFAULT);
|
||||
}
|
||||
|
@ -8993,9 +9010,9 @@ void Application::setDisplayPlugin(DisplayPluginPointer newDisplayPlugin) {
|
|||
cameraMenuChanged();
|
||||
}
|
||||
|
||||
// Remove the mirror camera option from menu if in HMD mode
|
||||
auto mirrorAction = menu->getActionForOption(MenuOption::FullscreenMirror);
|
||||
mirrorAction->setVisible(!isHmd);
|
||||
// Remove the selfie camera options from menu if in HMD mode
|
||||
auto selfieAction = menu->getActionForOption(MenuOption::SelfieCamera);
|
||||
selfieAction->setVisible(!isHmd);
|
||||
}
|
||||
|
||||
Q_ASSERT_X(_displayPlugin, "Application::updateDisplayMode", "could not find an activated display plugin");
|
||||
|
|
|
@ -177,19 +177,19 @@ Menu::Menu() {
|
|||
|
||||
firstPersonAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
|
||||
|
||||
// View > Third Person
|
||||
auto thirdPersonAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(
|
||||
viewMenu, MenuOption::ThirdPerson, 0,
|
||||
// View > Look At
|
||||
auto lookAtAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(
|
||||
viewMenu, MenuOption::LookAtCamera, 0,
|
||||
false, qApp, SLOT(cameraMenuChanged())));
|
||||
|
||||
thirdPersonAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
|
||||
lookAtAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
|
||||
|
||||
// View > Mirror
|
||||
auto viewMirrorAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(
|
||||
viewMenu, MenuOption::FullscreenMirror, 0,
|
||||
false, qApp, SLOT(cameraMenuChanged())));
|
||||
// View > Selfie
|
||||
auto selfieAction = cameraModeGroup->addAction(addCheckableActionToQMenuAndActionHash(
|
||||
viewMenu, MenuOption::SelfieCamera, 0,
|
||||
false, qApp, SLOT(cameraMenuChanged())));
|
||||
|
||||
viewMirrorAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
|
||||
selfieAction->setProperty(EXCLUSION_GROUP_KEY, QVariant::fromValue(cameraModeGroup));
|
||||
|
||||
viewMenu->addSeparator();
|
||||
|
||||
|
|
|
@ -129,6 +129,7 @@ namespace MenuOption {
|
|||
const QString Login = "Login/Sign Up";
|
||||
const QString Log = "Log";
|
||||
const QString LogExtraTimings = "Log Extra Timing Details";
|
||||
const QString LookAtCamera = "Third Person";
|
||||
const QString LowVelocityFilter = "Low Velocity Filter";
|
||||
const QString MeshVisible = "Draw Mesh";
|
||||
const QString MuteEnvironment = "Mute Environment";
|
||||
|
@ -181,6 +182,7 @@ namespace MenuOption {
|
|||
const QString RunTimingTests = "Run Timing Tests";
|
||||
const QString ScriptedMotorControl = "Enable Scripted Motor Control";
|
||||
const QString ShowTrackedObjects = "Show Tracked Objects";
|
||||
const QString SelfieCamera = "Selfie";
|
||||
const QString SendWrongDSConnectVersion = "Send wrong DS connect version";
|
||||
const QString SendWrongProtocolVersion = "Send wrong protocol version";
|
||||
const QString SetHomeLocation = "Set Home Location";
|
||||
|
@ -201,7 +203,7 @@ namespace MenuOption {
|
|||
const QString AnimStats = "Show Animation Stats";
|
||||
const QString StopAllScripts = "Stop All Scripts";
|
||||
const QString SuppressShortTimings = "Suppress Timings Less than 10ms";
|
||||
const QString ThirdPerson = "Third Person";
|
||||
const QString ThirdPerson = "Third Person Legacy";
|
||||
const QString ThreePointCalibration = "3 Point Calibration";
|
||||
const QString ThrottleFPSIfNotFocus = "Throttle FPS If Not Focus"; // FIXME - this value duplicated in Basic2DWindowOpenGLDisplayPlugin.cpp
|
||||
const QString ToggleHipsFollowing = "Toggle Hips Following";
|
||||
|
|
|
@ -99,6 +99,10 @@ static const QString USER_RECENTER_MODEL_FORCE_STAND = QStringLiteral("ForceStan
|
|||
static const QString USER_RECENTER_MODEL_AUTO = QStringLiteral("Auto");
|
||||
static const QString USER_RECENTER_MODEL_DISABLE_HMD_LEAN = QStringLiteral("DisableHMDLean");
|
||||
|
||||
const QString HEAD_BLENDING_NAME = "lookAroundAlpha";
|
||||
const QString HEAD_ALPHA_NAME = "additiveBlendAlpha";
|
||||
const float HEAD_ALPHA_BLENDING = 1.0f;
|
||||
|
||||
MyAvatar::SitStandModelType stringToUserRecenterModel(const QString& str) {
|
||||
if (str == USER_RECENTER_MODEL_FORCE_SIT) {
|
||||
return MyAvatar::ForceSit;
|
||||
|
@ -451,7 +455,7 @@ QByteArray MyAvatar::toByteArrayStateful(AvatarDataDetail dataDetail, bool dropF
|
|||
_globalBoundingBoxDimensions.y = _characterController.getCapsuleHalfHeight();
|
||||
_globalBoundingBoxDimensions.z = _characterController.getCapsuleRadius();
|
||||
_globalBoundingBoxOffset = _characterController.getCapsuleLocalOffset();
|
||||
if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) {
|
||||
if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) {
|
||||
// fake the avatar position that is sent up to the AvatarMixer
|
||||
glm::vec3 oldPosition = getWorldPosition();
|
||||
setWorldPosition(getSkeletonPosition());
|
||||
|
@ -948,6 +952,13 @@ void MyAvatar::simulate(float deltaTime, bool inView) {
|
|||
head->setPosition(headPosition);
|
||||
head->setScale(getModelScale());
|
||||
head->simulate(deltaTime);
|
||||
CameraMode mode = qApp->getCamera().getMode();
|
||||
if (_scriptControlsHeadLookAt || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) {
|
||||
updateHeadLookAt(deltaTime);
|
||||
} else if (_headLookAtActive){
|
||||
resetHeadLookAt();
|
||||
_headLookAtActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Record avatars movements.
|
||||
|
@ -2610,7 +2621,7 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN
|
|||
|
||||
glm::vec3 MyAvatar::getSkeletonPosition() const {
|
||||
CameraMode mode = qApp->getCamera().getMode();
|
||||
if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) {
|
||||
if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) {
|
||||
// The avatar is rotated PI about the yAxis, so we have to correct for it
|
||||
// to get the skeleton offset contribution in the world-frame.
|
||||
const glm::quat FLIP = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
|
@ -3133,7 +3144,6 @@ void MyAvatar::initAnimGraph() {
|
|||
}
|
||||
|
||||
emit animGraphUrlChanged(graphUrl);
|
||||
|
||||
_skeletonModel->getRig().initAnimGraph(graphUrl);
|
||||
_currentAnimGraphUrl.set(graphUrl);
|
||||
connect(&(_skeletonModel->getRig()), SIGNAL(onLoadComplete()), this, SLOT(animGraphLoaded()));
|
||||
|
@ -3416,11 +3426,19 @@ void MyAvatar::setRotationThreshold(float angleRadians) {
|
|||
}
|
||||
|
||||
void MyAvatar::updateOrientation(float deltaTime) {
|
||||
|
||||
// Smoothly rotate body with arrow keys
|
||||
float targetSpeed = getDriveKey(YAW) * _yawSpeed;
|
||||
CameraMode mode = qApp->getCamera().getMode();
|
||||
bool computeLookAt = (mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) && isReadyForPhysics() && !qApp->isHMDMode();
|
||||
if (computeLookAt) {
|
||||
// For "Look At" and "Selfie" camera modes we also smooth the yaw rotation from right-click mouse movement.
|
||||
float speedFromDeltaYaw = deltaTime > FLT_EPSILON ? getDriveKey(DELTA_YAW) / deltaTime : 0.0f;
|
||||
speedFromDeltaYaw *= _yawSpeed / YAW_SPEED_DEFAULT;
|
||||
targetSpeed += speedFromDeltaYaw;
|
||||
}
|
||||
|
||||
if (targetSpeed != 0.0f) {
|
||||
const float ROTATION_RAMP_TIMESCALE = 0.1f;
|
||||
const float ROTATION_RAMP_TIMESCALE = 0.5f;
|
||||
float blend = deltaTime / ROTATION_RAMP_TIMESCALE;
|
||||
if (blend > 1.0f) {
|
||||
blend = 1.0f;
|
||||
|
@ -3441,10 +3459,10 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
}
|
||||
}
|
||||
float totalBodyYaw = _bodyYawDelta * deltaTime;
|
||||
|
||||
// Rotate directly proportional to delta yaw and delta pitch from right-click mouse movement.
|
||||
totalBodyYaw += getDriveKey(DELTA_YAW) * _yawSpeed / YAW_SPEED_DEFAULT;
|
||||
|
||||
if (!computeLookAt) {
|
||||
// Rotate directly proportional to delta yaw and delta pitch from right-click mouse movement.
|
||||
totalBodyYaw += getDriveKey(DELTA_YAW) * _yawSpeed / YAW_SPEED_DEFAULT;
|
||||
}
|
||||
// Comfort Mode: If you press any of the left/right rotation drive keys or input, you'll
|
||||
// get an instantaneous 15 degree turn. If you keep holding the key down you'll get another
|
||||
// snap turn every half second.
|
||||
|
@ -3453,7 +3471,6 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
totalBodyYaw += getDriveKey(STEP_YAW);
|
||||
snapTurn = true;
|
||||
}
|
||||
|
||||
// Use head/HMD roll to turn while flying, but not when standing still.
|
||||
if (qApp->isHMDMode() && getCharacterController()->getState() == CharacterController::State::Hover && _hmdRollControlEnabled && hasDriveInput()) {
|
||||
|
||||
|
@ -3488,7 +3505,60 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
|
||||
// update body orientation by movement inputs
|
||||
glm::quat initialOrientation = getOrientationOutbound();
|
||||
setWorldOrientation(getWorldOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f))));
|
||||
glm::vec3 eyesPosition = getDefaultEyePosition();
|
||||
const float FPS = 60.0f;
|
||||
float timeScale = deltaTime * FPS;
|
||||
|
||||
bool faceForward = false;
|
||||
if (!computeLookAt) {
|
||||
setWorldOrientation(getWorldOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f))));
|
||||
_lookAtCameraTarget = eyesPosition + getWorldOrientation() * Vectors::FRONT;
|
||||
_lookAtYaw = getWorldOrientation();
|
||||
_lookAtPitch = Quaternions::IDENTITY;
|
||||
} else {
|
||||
// Compute new look at vectors
|
||||
if (totalBodyYaw != 0.0f) {
|
||||
_lookAtYaw = _lookAtYaw * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f)));
|
||||
}
|
||||
float pitchIncrement = getDriveKey(PITCH) * _pitchSpeed * deltaTime
|
||||
+ getDriveKey(DELTA_PITCH) * _pitchSpeed / PITCH_SPEED_DEFAULT;
|
||||
if (pitchIncrement != 0.0f) {
|
||||
glm::quat _previousLookAtPitch = _lookAtPitch;
|
||||
_lookAtPitch = _lookAtPitch * glm::quat(glm::radians(glm::vec3(pitchIncrement, 0.0f, 0.0f)));
|
||||
// Limit the camera horizontal pitch
|
||||
float MAX_LOOK_AT_PITCH_DEGREES = 80.0f;
|
||||
float pitchFromHorizont = glm::degrees(angleBetween(getLookAtRotation() * Vectors::FRONT, _lookAtYaw * Vectors::FRONT));
|
||||
if (pitchFromHorizont > MAX_LOOK_AT_PITCH_DEGREES) {
|
||||
_lookAtPitch = _previousLookAtPitch;
|
||||
}
|
||||
}
|
||||
bool isMovingFwdBwd = getDriveKey(TRANSLATE_Z) != 0.0f;
|
||||
bool isMovingSideways = getDriveKey(TRANSLATE_X) != 0.0f;
|
||||
bool isRotatingWhileSeated = isMovingSideways && _characterController.getSeated();
|
||||
faceForward = isMovingFwdBwd || (isMovingSideways && !isRotatingWhileSeated);
|
||||
// Blend the avatar orientation with the camera look at if moving forward.
|
||||
if (faceForward || _shouldTurnToFaceCamera) {
|
||||
const float REORIENT_FORWARD_BLEND = 0.25f;
|
||||
const float REORIENT_TURN_BLEND = 0.03f;
|
||||
const float DIAGONAL_TURN_BLEND = 0.02f;
|
||||
float blend = (_shouldTurnToFaceCamera ? REORIENT_TURN_BLEND : REORIENT_FORWARD_BLEND) * timeScale;
|
||||
if (blend > 1.0f) {
|
||||
blend = 1.0f;
|
||||
}
|
||||
glm::quat faceRotation = _lookAtYaw;
|
||||
if (isMovingFwdBwd && isMovingSideways) {
|
||||
// Reorient avatar to face camera diagonal
|
||||
blend = DIAGONAL_TURN_BLEND;
|
||||
float turnSign = getDriveKey(TRANSLATE_Z) < 0.0f ? -1.0f : 1.0f;
|
||||
turnSign = getDriveKey(TRANSLATE_X) > 0.0f ? -turnSign : turnSign;
|
||||
faceRotation = _lookAtYaw * glm::angleAxis(turnSign * 0.25f * PI, Vectors::UP);
|
||||
}
|
||||
setWorldOrientation(glm::slerp(getWorldOrientation(), faceRotation, blend));
|
||||
} else if (isRotatingWhileSeated) {
|
||||
float rotatingWhileSeatedYaw = -getDriveKey(TRANSLATE_X) * _yawSpeed * deltaTime;
|
||||
setWorldOrientation(getWorldOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, rotatingWhileSeatedYaw, 0.0f))));
|
||||
}
|
||||
}
|
||||
|
||||
if (snapTurn) {
|
||||
// Whether or not there is an existing smoothing going on, just reset the smoothing timer and set the starting position as the avatar's current position, then smooth to the new position.
|
||||
|
@ -3509,9 +3579,74 @@ void MyAvatar::updateOrientation(float deltaTime) {
|
|||
head->setBaseYaw(YAW(euler));
|
||||
head->setBasePitch(PITCH(euler));
|
||||
head->setBaseRoll(ROLL(euler));
|
||||
} else if (computeLookAt) {
|
||||
// Reset head orientation before applying the blending offset
|
||||
head->setBaseYaw(0.0f);
|
||||
head->setBasePitch(0.0f);
|
||||
head->setBaseRoll(0.0f);
|
||||
|
||||
glm::vec3 cameraVector = (faceForward ? _lookAtPitch * getWorldOrientation() : getLookAtRotation()) * Vectors::FRONT;
|
||||
glm::vec3 cameraYawVector = _lookAtYaw * Vectors::FRONT;
|
||||
|
||||
// Cap and attenuate head's lookat pitch angle
|
||||
const float START_LOOKING_UP_DEGREES = 5.0f;
|
||||
const float START_LOOKING_DOWN_DEGREES = 15.0f;
|
||||
const float MAX_UP_DOWN_DEGREES = 90.0f;
|
||||
|
||||
glm::vec3 avatarVectorUp = getWorldOrientation() * Vectors::UP;
|
||||
float upDownDot = glm::dot(cameraVector, avatarVectorUp);
|
||||
float upDownDegrees = MAX_UP_DOWN_DEGREES - glm::degrees(acosf(abs(upDownDot)));
|
||||
|
||||
float lookAttenuation = 0.0f;
|
||||
if (upDownDot <= 0.0f) {
|
||||
if (upDownDegrees > START_LOOKING_DOWN_DEGREES) {
|
||||
lookAttenuation = (upDownDegrees - START_LOOKING_DOWN_DEGREES) / (MAX_UP_DOWN_DEGREES - START_LOOKING_DOWN_DEGREES);
|
||||
}
|
||||
} else {
|
||||
if (upDownDegrees > START_LOOKING_UP_DEGREES) {
|
||||
lookAttenuation = (upDownDegrees - START_LOOKING_UP_DEGREES) / (MAX_UP_DOWN_DEGREES - START_LOOKING_UP_DEGREES);
|
||||
}
|
||||
}
|
||||
glm::vec3 avatarVectorFront = getWorldOrientation() * Vectors::FRONT;
|
||||
float frontBackDot = glm::dot(cameraYawVector, avatarVectorFront);
|
||||
|
||||
glm::vec3 avatarVectorRight = getWorldOrientation() * Vectors::RIGHT;
|
||||
float leftRightDot = glm::dot(cameraYawVector, avatarVectorRight);
|
||||
|
||||
const float REORIENT_ANGLE = 65.0f;
|
||||
const float TRIGGER_REORIENT_ANGLE = 45.0f;
|
||||
glm::vec3 ajustedYawVector = cameraYawVector;
|
||||
if (frontBackDot < 0.0f) {
|
||||
ajustedYawVector = (leftRightDot < 0.0f ? -avatarVectorRight : avatarVectorRight);
|
||||
cameraVector = (ajustedYawVector * _lookAtPitch) * Vectors::FRONT;
|
||||
if (frontBackDot < -glm::sin(glm::radians(TRIGGER_REORIENT_ANGLE))) {
|
||||
_shouldTurnToFaceCamera = true;
|
||||
}
|
||||
} else if (frontBackDot > glm::sin(glm::radians(REORIENT_ANGLE))) {
|
||||
_shouldTurnToFaceCamera = false;
|
||||
}
|
||||
|
||||
cameraVector = glm::mix(cameraVector, ajustedYawVector, 1.0f - lookAttenuation);
|
||||
// Calculate the camera target point.
|
||||
|
||||
glm::vec3 targetPoint = eyesPosition + glm::normalize(cameraVector);
|
||||
|
||||
const float LOOKAT_MIX_ALPHA = 0.25f;
|
||||
|
||||
if (getDriveKey(TRANSLATE_Y) == 0.0f) {
|
||||
// Approximate the head's look at vector to the camera look at vector with some delay.
|
||||
float mixAlpha = LOOKAT_MIX_ALPHA * timeScale;
|
||||
if (mixAlpha > 1.0f) {
|
||||
mixAlpha = 1.0f;
|
||||
}
|
||||
_lookAtCameraTarget = glm::mix(_lookAtCameraTarget, targetPoint, mixAlpha);
|
||||
} else {
|
||||
_lookAtCameraTarget = targetPoint;
|
||||
}
|
||||
_headLookAtActive = true;
|
||||
} else {
|
||||
head->setBaseYaw(0.0f);
|
||||
head->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime
|
||||
head->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime
|
||||
+ getDriveKey(DELTA_PITCH) * _pitchSpeed / PITCH_SPEED_DEFAULT);
|
||||
head->setBaseRoll(0.0f);
|
||||
}
|
||||
|
@ -3543,7 +3678,7 @@ float MyAvatar::calculateGearedSpeed(const float driveKey) {
|
|||
glm::vec3 MyAvatar::scaleMotorSpeed(const glm::vec3 forward, const glm::vec3 right) {
|
||||
float stickFullOn = 0.85f;
|
||||
auto zSpeed = getDriveKey(TRANSLATE_Z);
|
||||
auto xSpeed = getDriveKey(TRANSLATE_X);
|
||||
auto xSpeed = !_characterController.getSeated() ? getDriveKey(TRANSLATE_X) : 0.0f;
|
||||
glm::vec3 direction;
|
||||
if (!useAdvancedMovementControls() && qApp->isHMDMode()) {
|
||||
// Walking disabled in settings.
|
||||
|
@ -3581,6 +3716,10 @@ glm::vec3 MyAvatar::scaleMotorSpeed(const glm::vec3 forward, const glm::vec3 rig
|
|||
} else {
|
||||
// Desktop mode.
|
||||
direction = (zSpeed * forward) + (xSpeed * right);
|
||||
if (qApp->getCamera().getMode() == CAMERA_MODE_LOOK_AT && zSpeed != 0.0f && xSpeed != 0.0f){
|
||||
direction = (zSpeed * forward);
|
||||
}
|
||||
|
||||
auto length = glm::length(direction);
|
||||
if (length > EPSILON) {
|
||||
direction /= length;
|
||||
|
@ -5236,9 +5375,13 @@ glm::quat MyAvatar::getOrientationForAudio() {
|
|||
glm::quat result;
|
||||
|
||||
switch (_audioListenerMode) {
|
||||
case AudioListenerMode::FROM_HEAD:
|
||||
result = getHead()->getFinalOrientationInWorldFrame();
|
||||
case AudioListenerMode::FROM_HEAD: {
|
||||
// Using the camera's orientation instead, when the current mode is controlling the avatar's head.
|
||||
CameraMode mode = qApp->getCamera().getMode();
|
||||
bool headFollowsCamera = mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE;
|
||||
result = headFollowsCamera ? qApp->getCamera().getOrientation() : getHead()->getFinalOrientationInWorldFrame();
|
||||
break;
|
||||
}
|
||||
case AudioListenerMode::FROM_CAMERA:
|
||||
result = qApp->getCamera().getOrientation();
|
||||
break;
|
||||
|
@ -6334,7 +6477,6 @@ void MyAvatar::sendPacket(const QUuid& entityID) const {
|
|||
|
||||
void MyAvatar::setSitDriveKeysStatus(bool enabled) {
|
||||
const std::vector<DriveKeys> DISABLED_DRIVE_KEYS_DURING_SIT = {
|
||||
DriveKeys::TRANSLATE_X,
|
||||
DriveKeys::TRANSLATE_Y,
|
||||
DriveKeys::TRANSLATE_Z,
|
||||
DriveKeys::STEP_TRANSLATE_X,
|
||||
|
@ -6512,3 +6654,70 @@ void MyAvatar::updateLookAtPosition(FaceTracker* faceTracker, Camera& myCamera)
|
|||
|
||||
getHead()->setLookAtPosition(lookAtSpot);
|
||||
}
|
||||
|
||||
void MyAvatar::resetHeadLookAt() {
|
||||
if (_skeletonModelLoaded) {
|
||||
_skeletonModel->getRig().setDirectionalBlending(HEAD_BLENDING_NAME, glm::vec3(),
|
||||
HEAD_ALPHA_NAME, HEAD_ALPHA_BLENDING);
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::updateHeadLookAt(float deltaTime) {
|
||||
if (_skeletonModelLoaded) {
|
||||
glm::vec3 lookAtTarget = _scriptControlsHeadLookAt ? _lookAtScriptTarget : _lookAtCameraTarget;
|
||||
glm::vec3 avatarXVector = glm::normalize(getWorldOrientation() * Vectors::UNIT_X);
|
||||
glm::vec3 avatarYVector = glm::normalize(getWorldOrientation() * Vectors::UNIT_Y);
|
||||
glm::vec3 avatarZVector = glm::normalize(getWorldOrientation() * Vectors::UNIT_Z);
|
||||
glm::vec3 headToTargetVector = lookAtTarget - getDefaultEyePosition();
|
||||
if (glm::length(headToTargetVector) > EPSILON) {
|
||||
headToTargetVector = glm::normalize(headToTargetVector);
|
||||
} else {
|
||||
// The target point is the avatar head
|
||||
return;
|
||||
}
|
||||
|
||||
float xDot = glm::dot(avatarXVector, headToTargetVector);
|
||||
float yDot = glm::dot(avatarYVector, headToTargetVector);
|
||||
float zDot = glm::dot(avatarZVector, headToTargetVector);
|
||||
// Force the head to look at one of the sides when the look at point is behind the avatar
|
||||
if (zDot > 0.0f && xDot != 0.0f) {
|
||||
//xDot /= fabsf(xDot);
|
||||
}
|
||||
|
||||
// Make sure dot products are in range to avoid acosf returning NaN
|
||||
xDot = glm::min(glm::max(xDot, -1.0f), 1.0f);
|
||||
yDot = glm::min(glm::max(yDot, -1.0f), 1.0f);
|
||||
|
||||
float xAngle = acosf(xDot);
|
||||
float yAngle = acosf(yDot);
|
||||
|
||||
// xBlend and yBlend are the values from -1.0 to 1.0 that set the directional blending.
|
||||
// We compute them using the angles (0 to PI/2) => (1.0 to 0.0) and (PI/2 to PI) => (0.0 to -1.0)
|
||||
float xBlend = -(xAngle - 0.5f * PI) / (0.5f * PI);
|
||||
float yBlend = -(yAngle - 0.5f * PI) / (0.5f * PI);
|
||||
glm::vec3 lookAtBlend = glm::vec3(xBlend, yBlend, 0.0f);
|
||||
_skeletonModel->getRig().setDirectionalBlending(HEAD_BLENDING_NAME, lookAtBlend,
|
||||
HEAD_ALPHA_NAME, HEAD_ALPHA_BLENDING);
|
||||
|
||||
if (_scriptControlsHeadLookAt) {
|
||||
_scriptHeadControlTimer += deltaTime;
|
||||
if (_scriptHeadControlTimer > MAX_LOOK_AT_TIME_SCRIPT_CONTROL) {
|
||||
_scriptHeadControlTimer = 0.0f;
|
||||
_scriptControlsHeadLookAt = false;
|
||||
_lookAtCameraTarget = _lookAtScriptTarget;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MyAvatar::setHeadLookAt(const glm::vec3& lookAtTarget) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
BLOCKING_INVOKE_METHOD(this, "setHeadLookAt",
|
||||
Q_ARG(const glm::vec3&, lookAtTarget));
|
||||
return;
|
||||
}
|
||||
_headLookAtActive = true;
|
||||
_scriptControlsHeadLookAt = true;
|
||||
_scriptHeadControlTimer = 0.0f;
|
||||
_lookAtScriptTarget = lookAtTarget;
|
||||
}
|
||||
|
|
|
@ -1749,6 +1749,24 @@ public:
|
|||
glm::vec3 getNextPosition() { return _goToPending ? _goToPosition : getWorldPosition(); }
|
||||
void prepareAvatarEntityDataForReload();
|
||||
|
||||
/**jsdoc
|
||||
* Turn the avatar's head until it faces the target point within the 90/-90 degree range.
|
||||
* Once this method is called, API calls will have full control of the head for a limited time.
|
||||
* If this method is not called for two seconds, the engine will regain control of the head.
|
||||
* @function MyAvatar.setHeadLookAt
|
||||
* @param {Vec3} lookAtTarget - The target point in world coordinates.
|
||||
*/
|
||||
Q_INVOKABLE void setHeadLookAt(const glm::vec3& lookAtTarget);
|
||||
|
||||
/**jsdoc
|
||||
* Returns the current head look at target point in world coordinates.
|
||||
* @function MyAvatar.getHeadLookAt
|
||||
* @returns {Vec3} Default position between your avatar's eyes in world coordinates.
|
||||
*/
|
||||
Q_INVOKABLE glm::vec3 getHeadLookAt() { return _lookAtCameraTarget; }
|
||||
|
||||
glm::quat getLookAtRotation() { return _lookAtYaw * _lookAtPitch; }
|
||||
|
||||
/**jsdoc
|
||||
* Creates a new grab that grabs an entity.
|
||||
* @function MyAvatar.grab
|
||||
|
@ -2626,6 +2644,21 @@ private:
|
|||
|
||||
glm::vec3 _trackedHeadPosition;
|
||||
|
||||
const float MAX_LOOK_AT_TIME_SCRIPT_CONTROL = 2.0f;
|
||||
glm::quat _lookAtPitch;
|
||||
glm::quat _lookAtYaw;
|
||||
glm::vec3 _lookAtCameraTarget;
|
||||
glm::vec3 _lookAtScriptTarget;
|
||||
bool _headLookAtActive { false };
|
||||
bool _shouldTurnToFaceCamera { false };
|
||||
bool _scriptControlsHeadLookAt { false };
|
||||
float _scriptHeadControlTimer { 0.0f };
|
||||
|
||||
// LookAt camera data
|
||||
float _selfieTriggerAngle { 55.0f };
|
||||
float _frontLookAtSpeed { 0.15f };
|
||||
float _backLookAtSpeed { 0.25f };
|
||||
|
||||
Setting::Handle<float> _realWorldFieldOfView;
|
||||
Setting::Handle<bool> _useAdvancedMovementControls;
|
||||
Setting::Handle<bool> _showPlayArea;
|
||||
|
@ -2650,6 +2683,8 @@ private:
|
|||
void initHeadBones();
|
||||
void initAnimGraph();
|
||||
void initFlowFromFST();
|
||||
void updateHeadLookAt(float deltaTime);
|
||||
void resetHeadLookAt();
|
||||
|
||||
// Avatar Preferences
|
||||
QUrl _fullAvatarURLFromPreferences;
|
||||
|
|
|
@ -1658,7 +1658,7 @@ void Rig::updateHead(bool headEnabled, bool hipsEnabled, const AnimPose& headPos
|
|||
_animVars.set("splineIKEnabled", false);
|
||||
_animVars.unset("headPosition");
|
||||
_animVars.set("headRotation", headPose.rot());
|
||||
_animVars.set("headType", (int)IKTarget::Type::RotationOnly);
|
||||
_animVars.set("headType", (int)IKTarget::Type::Unknown);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2645,3 +2645,8 @@ float Rig::getUnscaledEyeHeight() const {
|
|||
return DEFAULT_AVATAR_EYE_HEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
void Rig::setDirectionalBlending(const QString& targetName, const glm::vec3& blendingTarget, const QString& alphaName, float alpha) {
|
||||
_animVars.set(targetName, blendingTarget);
|
||||
_animVars.set(alphaName, alpha);
|
||||
}
|
||||
|
|
|
@ -254,6 +254,7 @@ public:
|
|||
int getOverrideJointCount() const;
|
||||
bool getFlowActive() const;
|
||||
bool getNetworkGraphActive() const;
|
||||
void setDirectionalBlending(const QString& targetName, const glm::vec3& blendingTarget, const QString& alphaName, float alpha);
|
||||
|
||||
signals:
|
||||
void onLoadComplete();
|
||||
|
|
|
@ -35,6 +35,18 @@
|
|||
* your avatar.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><strong>Look At</strong></td>
|
||||
* <td><code>"look at"</code></td>
|
||||
* <td>The camera is positioned behind your avatar. The camera moves and rotates independently from your avatar.
|
||||
* The avatar's head always faces the camera look at point.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><strong>Selfie</strong></td>
|
||||
* <td><code>"selfie"</code></td>
|
||||
* <td>The camera is positioned in front of your avatar. The camera moves and rotates independently from your avatar.
|
||||
* Your avatar's head is always facing the camera.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><strong>Mirror</strong></td>
|
||||
* <td><code>"mirror"</code></td>
|
||||
* <td>The camera is positioned such that you are looking directly at your avatar. The camera moves and rotates with your
|
||||
|
@ -67,6 +79,10 @@ CameraMode stringToMode(const QString& mode) {
|
|||
return CAMERA_MODE_INDEPENDENT;
|
||||
} else if (mode == "entity") {
|
||||
return CAMERA_MODE_ENTITY;
|
||||
} else if (mode == "look at") {
|
||||
return CAMERA_MODE_LOOK_AT;
|
||||
} else if (mode == "selfie") {
|
||||
return CAMERA_MODE_SELFIE;
|
||||
}
|
||||
return CAMERA_MODE_NULL;
|
||||
}
|
||||
|
@ -82,6 +98,10 @@ QString modeToString(CameraMode mode) {
|
|||
return "independent";
|
||||
} else if (mode == CAMERA_MODE_ENTITY) {
|
||||
return "entity";
|
||||
} else if (mode == CAMERA_MODE_LOOK_AT) {
|
||||
return "look at";
|
||||
} else if (mode == CAMERA_MODE_SELFIE) {
|
||||
return "selfie";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ enum CameraMode
|
|||
CAMERA_MODE_MIRROR,
|
||||
CAMERA_MODE_INDEPENDENT,
|
||||
CAMERA_MODE_ENTITY,
|
||||
CAMERA_MODE_LOOK_AT,
|
||||
CAMERA_MODE_SELFIE,
|
||||
NUM_CAMERA_MODES
|
||||
};
|
||||
|
||||
|
@ -182,7 +184,7 @@ private:
|
|||
void recompose();
|
||||
void decompose();
|
||||
|
||||
CameraMode _mode{ CAMERA_MODE_THIRD_PERSON };
|
||||
CameraMode _mode{ CAMERA_MODE_LOOK_AT };
|
||||
glm::mat4 _transform;
|
||||
glm::mat4 _projection;
|
||||
|
||||
|
|
Loading…
Reference in a new issue