mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-07 01:02:35 +02:00
Added HMD Avatar Alignment Type
Adds a new API to set this type, and an option to the Avatar App. * "eye" - Match the user eyes with the avatar eyes. The previous default behavior. If the avatar skull is larger then the user, it can cause the avatars body to lift off of the ground when the user looks down. * "head" - Match the user head with the avatar head. The new default behavior. This prevents the body from lifting off of the ground while wearing a large headed avatar. But can cause an offset between the users eyes and the avatars eyes.
This commit is contained in:
parent
39f4af505c
commit
5d606981de
10 changed files with 146 additions and 11 deletions
|
@ -254,6 +254,7 @@ Rectangle {
|
|||
onSaveClicked: function() {
|
||||
var avatarSettings = {
|
||||
dominantHand : settings.dominantHandIsLeft ? 'left' : 'right',
|
||||
hmdAvatarAlignmentType : settings.hmdAvatarAlignmentTypeIsEyes ? 'eyes' : 'head',
|
||||
collisionsEnabled : settings.environmentCollisionsOn,
|
||||
otherAvatarsCollisionsEnabled : settings.otherAvatarsCollisionsOn,
|
||||
animGraphOverrideUrl : settings.avatarAnimationOverrideJSON,
|
||||
|
|
|
@ -36,6 +36,7 @@ Rectangle {
|
|||
property real scaleValue: scaleSlider.value / 10
|
||||
property alias dominantHandIsLeft: leftHandRadioButton.checked
|
||||
property alias otherAvatarsCollisionsOn: otherAvatarsCollisionsEnabledCheckBox.checked
|
||||
property alias hmdAvatarAlignmentTypeIsEyes: eyesRadioButton.checked
|
||||
property alias environmentCollisionsOn: environmentCollisionsEnabledCheckBox.checked
|
||||
property alias avatarAnimationOverrideJSON: avatarAnimationUrlInputText.text
|
||||
property alias avatarAnimationJSON: avatarAnimationUrlInputText.placeholderText
|
||||
|
@ -61,6 +62,11 @@ Rectangle {
|
|||
if (settings.collisionsEnabled) {
|
||||
environmentCollisionsEnabledCheckBox.checked = true;
|
||||
}
|
||||
if (settings.hmdAvatarAlignmentType === 'eyes') {
|
||||
eyesRadioButton.checked = true;
|
||||
} else {
|
||||
headRadioButton.checked = true;
|
||||
}
|
||||
|
||||
avatarAnimationJSON = settings.animGraphUrl;
|
||||
avatarAnimationOverrideJSON = settings.animGraphOverrideUrl;
|
||||
|
@ -256,7 +262,7 @@ Rectangle {
|
|||
text: "Right"
|
||||
boxSize: 20
|
||||
}
|
||||
|
||||
|
||||
HifiConstants {
|
||||
id: hifi
|
||||
}
|
||||
|
@ -294,6 +300,52 @@ Rectangle {
|
|||
Layout.leftMargin: 60
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
}
|
||||
|
||||
// TextStyle9
|
||||
RalewaySemiBold {
|
||||
size: 17;
|
||||
Layout.row: 4
|
||||
Layout.column: 0
|
||||
text: "HMD Alignment"
|
||||
}
|
||||
|
||||
ButtonGroup {
|
||||
id: headEyes
|
||||
}
|
||||
|
||||
HifiControlsUit.RadioButton {
|
||||
id: headRadioButton
|
||||
|
||||
Layout.row: 4
|
||||
Layout.column: 1
|
||||
Layout.leftMargin: -40
|
||||
|
||||
ButtonGroup.group: headEyes
|
||||
checked: true
|
||||
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
fontSize: 17
|
||||
letterSpacing: 1.4
|
||||
text: "Head"
|
||||
boxSize: 20
|
||||
}
|
||||
|
||||
HifiControlsUit.RadioButton {
|
||||
id: eyesRadioButton
|
||||
|
||||
Layout.row: 4
|
||||
Layout.column: 2
|
||||
Layout.rightMargin: 20
|
||||
|
||||
ButtonGroup.group: headEyes
|
||||
|
||||
colorScheme: hifi.colorSchemes.light
|
||||
fontSize: 17
|
||||
letterSpacing: 1.4
|
||||
text: "Eyes"
|
||||
boxSize: 20
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
|
|
@ -5922,6 +5922,13 @@ void Application::update(float deltaTime) {
|
|||
|
||||
auto userInputMapper = DependencyManager::get<UserInputMapper>();
|
||||
|
||||
controller::HmdAvatarAlignmentType hmdAvatarAlignmentType;
|
||||
if (myAvatar->getHmdAvatarAlignmentType() == "eyes") {
|
||||
hmdAvatarAlignmentType = controller::HmdAvatarAlignmentType::Eyes;
|
||||
} else {
|
||||
hmdAvatarAlignmentType = controller::HmdAvatarAlignmentType::Head;
|
||||
}
|
||||
|
||||
controller::InputCalibrationData calibrationData = {
|
||||
myAvatar->getSensorToWorldMatrix(),
|
||||
createMatFromQuatAndPos(myAvatar->getWorldOrientation(), myAvatar->getWorldPosition()),
|
||||
|
@ -5935,7 +5942,8 @@ void Application::update(float deltaTime) {
|
|||
myAvatar->getRightArmCalibrationMat(),
|
||||
myAvatar->getLeftArmCalibrationMat(),
|
||||
myAvatar->getRightHandCalibrationMat(),
|
||||
myAvatar->getLeftHandCalibrationMat()
|
||||
myAvatar->getLeftHandCalibrationMat(),
|
||||
hmdAvatarAlignmentType
|
||||
};
|
||||
|
||||
InputPluginPointer keyboardMousePlugin;
|
||||
|
|
|
@ -124,6 +124,7 @@ MyAvatar::MyAvatar(QThread* thread) :
|
|||
_prevShouldDrawHead(true),
|
||||
_audioListenerMode(FROM_HEAD),
|
||||
_dominantHandSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "dominantHand", DOMINANT_RIGHT_HAND),
|
||||
_hmdAvatarAlignmentTypeSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "hmdAvatarAlignmentType", DEFAULT_HMD_AVATAR_ALIGNMENT_TYPE),
|
||||
_headPitchSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "", 0.0f),
|
||||
_scaleSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "scale", _targetScale),
|
||||
_yawSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "yawSpeed", _yawSpeed),
|
||||
|
@ -283,10 +284,25 @@ MyAvatar::~MyAvatar() {
|
|||
_lookAtTargetAvatar.reset();
|
||||
}
|
||||
|
||||
QString MyAvatar::getDominantHand() const {
|
||||
return _dominantHand.get();
|
||||
}
|
||||
|
||||
void MyAvatar::setDominantHand(const QString& hand) {
|
||||
if (hand == DOMINANT_LEFT_HAND || hand == DOMINANT_RIGHT_HAND) {
|
||||
_dominantHand = hand;
|
||||
emit dominantHandChanged(_dominantHand);
|
||||
_dominantHand.set(hand);
|
||||
emit dominantHandChanged(hand);
|
||||
}
|
||||
}
|
||||
|
||||
QString MyAvatar::getHmdAvatarAlignmentType() const {
|
||||
return _hmdAvatarAlignmentType.get();
|
||||
}
|
||||
|
||||
void MyAvatar::setHmdAvatarAlignmentType(const QString& type) {
|
||||
if (type != _hmdAvatarAlignmentType.get()) {
|
||||
_hmdAvatarAlignmentType.set(type);
|
||||
emit hmdAvatarAlignmentTypeChanged(type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -374,6 +390,7 @@ void MyAvatar::resetSensorsAndBody() {
|
|||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "resetSensorsAndBody");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
qApp->getActiveDisplayPlugin()->resetSensors();
|
||||
|
@ -1273,7 +1290,8 @@ void MyAvatar::resizeAvatarEntitySettingHandles(unsigned int avatarEntityIndex)
|
|||
}
|
||||
|
||||
void MyAvatar::saveData() {
|
||||
_dominantHandSetting.set(_dominantHand);
|
||||
_dominantHandSetting.set(getDominantHand());
|
||||
_hmdAvatarAlignmentTypeSetting.set(getHmdAvatarAlignmentType());
|
||||
_headPitchSetting.set(getHead()->getBasePitch());
|
||||
_scaleSetting.set(_targetScale);
|
||||
_yawSpeedSetting.set(_yawSpeed);
|
||||
|
@ -1444,6 +1462,7 @@ void MyAvatar::loadData() {
|
|||
setCollisionSoundURL(_collisionSoundURLSetting.get(QUrl(DEFAULT_AVATAR_COLLISION_SOUND_URL)).toString());
|
||||
setSnapTurn(_useSnapTurnSetting.get());
|
||||
setDominantHand(_dominantHandSetting.get(DOMINANT_RIGHT_HAND).toLower());
|
||||
setHmdAvatarAlignmentType(_hmdAvatarAlignmentTypeSetting.get(DEFAULT_HMD_AVATAR_ALIGNMENT_TYPE).toLower());
|
||||
setUserHeight(_userHeightSetting.get(DEFAULT_AVATAR_HEIGHT));
|
||||
setTargetScale(_scaleSetting.get());
|
||||
|
||||
|
|
|
@ -252,6 +252,7 @@ class MyAvatar : public Avatar {
|
|||
|
||||
const QString DOMINANT_LEFT_HAND = "left";
|
||||
const QString DOMINANT_RIGHT_HAND = "right";
|
||||
const QString DEFAULT_HMD_AVATAR_ALIGNMENT_TYPE = "head";
|
||||
|
||||
public:
|
||||
enum DriveKeys {
|
||||
|
@ -512,7 +513,18 @@ public:
|
|||
* @function MyAvatar.getDominantHand
|
||||
* @returns {string}
|
||||
*/
|
||||
Q_INVOKABLE QString getDominantHand() const { return _dominantHand; }
|
||||
Q_INVOKABLE QString getDominantHand() const;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setHmdAvatarAlignmentType
|
||||
* @param {string} hand
|
||||
*/
|
||||
Q_INVOKABLE void setHmdAvatarAlignmentType(const QString& hand);
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setHmdAvatarAlignmentType
|
||||
* @returns {string}
|
||||
*/
|
||||
Q_INVOKABLE QString getHmdAvatarAlignmentType() const;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setCenterOfGravityModelEnabled
|
||||
|
@ -1565,6 +1577,13 @@ signals:
|
|||
*/
|
||||
void dominantHandChanged(const QString& hand);
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.hmdAvatarAlignmentTypeChanged
|
||||
* @param {string} type
|
||||
* @returns {Signal}
|
||||
*/
|
||||
void hmdAvatarAlignmentTypeChanged(const QString& type);
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.sensorToWorldScaleChanged
|
||||
* @param {number} scale
|
||||
|
@ -1752,7 +1771,8 @@ private:
|
|||
ThreadSafeValueCache<QUrl> _prefOverrideAnimGraphUrl;
|
||||
QUrl _fstAnimGraphOverrideUrl;
|
||||
bool _useSnapTurn { true };
|
||||
QString _dominantHand { DOMINANT_RIGHT_HAND };
|
||||
ThreadSafeValueCache<QString> _dominantHand { DOMINANT_RIGHT_HAND };
|
||||
ThreadSafeValueCache<QString> _hmdAvatarAlignmentType { DEFAULT_HMD_AVATAR_ALIGNMENT_TYPE };
|
||||
|
||||
const float ROLL_CONTROL_DEAD_ZONE_DEFAULT = 8.0f; // degrees
|
||||
const float ROLL_CONTROL_RATE_DEFAULT = 114.0f; // degrees / sec
|
||||
|
@ -1922,6 +1942,7 @@ private:
|
|||
bool _skeletonModelLoaded { false };
|
||||
|
||||
Setting::Handle<QString> _dominantHandSetting;
|
||||
Setting::Handle<QString> _hmdAvatarAlignmentTypeSetting;
|
||||
Setting::Handle<float> _headPitchSetting;
|
||||
Setting::Handle<float> _scaleSetting;
|
||||
Setting::Handle<float> _yawSpeedSetting;
|
||||
|
|
|
@ -15,6 +15,11 @@
|
|||
|
||||
namespace controller {
|
||||
|
||||
enum class HmdAvatarAlignmentType {
|
||||
Eyes = 0, // align the user's eyes with the avatars eyes
|
||||
Head // align the user's head with the avatars head
|
||||
};
|
||||
|
||||
struct InputCalibrationData {
|
||||
glm::mat4 sensorToWorldMat; // sensor to world
|
||||
glm::mat4 avatarMat; // avatar to world
|
||||
|
@ -29,6 +34,7 @@ struct InputCalibrationData {
|
|||
glm::mat4 defaultLeftArm; // default pose for leftArm joint in sensor space
|
||||
glm::mat4 defaultRightHand; // default pose for rightHand joint in sensor space
|
||||
glm::mat4 defaultLeftHand; // default pose for leftHand joint in sensor space
|
||||
HmdAvatarAlignmentType hmdAvatarAlignmentType;
|
||||
};
|
||||
|
||||
enum class ChannelType {
|
||||
|
|
|
@ -44,7 +44,7 @@ const float DEFAULT_AVATAR_RIGHTHAND_MASS = 2.0f;
|
|||
|
||||
// Used when avatar is missing joints... (avatar space)
|
||||
const glm::quat DEFAULT_AVATAR_MIDDLE_EYE_ROT { Quaternions::Y_180 };
|
||||
const glm::vec3 DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET = { 0.0f, 0.06f, -0.09f };
|
||||
const glm::vec3 DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET = { 0.0f, 0.064f, 0.084f };
|
||||
const glm::vec3 DEFAULT_AVATAR_HEAD_POS { 0.0f, 0.53f, 0.0f };
|
||||
const glm::quat DEFAULT_AVATAR_HEAD_ROT { Quaternions::Y_180 };
|
||||
const glm::vec3 DEFAULT_AVATAR_RIGHTARM_POS { -0.134824f, 0.396348f, -0.0515777f };
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <controllers/UserInputMapper.h>
|
||||
#include <controllers/StandardControls.h>
|
||||
|
||||
#include <AvatarConstants.h>
|
||||
#include <PerfStat.h>
|
||||
#include <PathUtils.h>
|
||||
#include <NumericalConstants.h>
|
||||
|
@ -327,8 +328,14 @@ void OculusControllerManager::TouchDevice::handleHeadPose(float deltaTime,
|
|||
ovr::toGlm(headPose.AngularVelocity));
|
||||
|
||||
glm::mat4 sensorToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat;
|
||||
glm::mat4 defaultHeadOffset = glm::inverse(inputCalibrationData.defaultCenterEyeMat) *
|
||||
inputCalibrationData.defaultHeadMat;
|
||||
glm::mat4 defaultHeadOffset;
|
||||
if (inputCalibrationData.hmdAvatarAlignmentType == controller::HmdAvatarAlignmentType::Eyes) {
|
||||
// align the eyes of the user with the eyes of the avatar
|
||||
defaultHeadOffset = glm::inverse(inputCalibrationData.defaultCenterEyeMat) * inputCalibrationData.defaultHeadMat;
|
||||
} else {
|
||||
// align the head of the user with the head of the avatar
|
||||
defaultHeadOffset = createMatFromQuatAndPos(Quaternions::IDENTITY, -DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET);
|
||||
}
|
||||
|
||||
pose.valid = true;
|
||||
_poseStateMap[controller::HEAD] = pose.postTransform(defaultHeadOffset).transform(sensorToAvatar);
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <SettingHandle.h>
|
||||
#include <OffscreenUi.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <AvatarConstants.h>
|
||||
#include <glm/ext.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <ui-plugins/PluginContainer.h>
|
||||
|
@ -1024,7 +1025,16 @@ void ViveControllerManager::InputDevice::handleHeadPoseEvent(const controller::I
|
|||
//perform a 180 flip to make the HMD face the +z instead of -z, beacuse the head faces +z
|
||||
glm::mat4 matYFlip = mat * Matrices::Y_180;
|
||||
controller::Pose pose(extractTranslation(matYFlip), glmExtractRotation(matYFlip), linearVelocity, angularVelocity);
|
||||
glm::mat4 defaultHeadOffset = glm::inverse(inputCalibrationData.defaultCenterEyeMat) * inputCalibrationData.defaultHeadMat;
|
||||
|
||||
glm::mat4 defaultHeadOffset;
|
||||
if (inputCalibrationData.hmdAvatarAlignmentType == controller::HmdAvatarAlignmentType::Eyes) {
|
||||
// align the eyes of the user with the eyes of the avatar
|
||||
defaultHeadOffset = glm::inverse(inputCalibrationData.defaultCenterEyeMat) * inputCalibrationData.defaultHeadMat;
|
||||
} else {
|
||||
// align the head of the user with the head of the avatar
|
||||
defaultHeadOffset = createMatFromQuatAndPos(Quaternions::IDENTITY, -DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET);
|
||||
}
|
||||
|
||||
glm::mat4 sensorToAvatar = glm::inverse(inputCalibrationData.avatarMat) * inputCalibrationData.sensorToWorldMat;
|
||||
_poseStateMap[controller::HEAD] = pose.postTransform(defaultHeadOffset).transform(sensorToAvatar);
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ function getMyAvatar() {
|
|||
function getMyAvatarSettings() {
|
||||
return {
|
||||
dominantHand: MyAvatar.getDominantHand(),
|
||||
hmdAvatarAlignmentType: MyAvatar.getHmdAvatarAlignmentType(),
|
||||
collisionsEnabled: MyAvatar.getCollisionsEnabled(),
|
||||
otherAvatarsCollisionsEnabled: MyAvatar.getOtherAvatarsCollisionsEnabled(),
|
||||
collisionSoundUrl : MyAvatar.collisionSoundURL,
|
||||
|
@ -129,6 +130,13 @@ function onDominantHandChanged(dominantHand) {
|
|||
}
|
||||
}
|
||||
|
||||
function onHmdAvatarAlignmentTypeChanged(type) {
|
||||
if (currentAvatarSettings.hmdAvatarAlignmentType !== type) {
|
||||
currentAvatarSettings.hmdAvatarAlignmentType = type;
|
||||
sendToQml({'method' : 'settingChanged', 'name' : 'hmdAvatarAlignmentType', 'value' : type});
|
||||
}
|
||||
}
|
||||
|
||||
function onCollisionsEnabledChanged(enabled) {
|
||||
if(currentAvatarSettings.collisionsEnabled !== enabled) {
|
||||
currentAvatarSettings.collisionsEnabled = enabled;
|
||||
|
@ -331,6 +339,7 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See
|
|||
currentAvatar.avatarScale = message.avatarScale;
|
||||
|
||||
MyAvatar.setDominantHand(message.settings.dominantHand);
|
||||
MyAvatar.setHmdAvatarAlignmentType(message.settings.hmdAvatarAlignmentType);
|
||||
MyAvatar.setOtherAvatarsCollisionsEnabled(message.settings.otherAvatarsCollisionsEnabled);
|
||||
MyAvatar.setCollisionsEnabled(message.settings.collisionsEnabled);
|
||||
MyAvatar.collisionSoundURL = message.settings.collisionSoundUrl;
|
||||
|
@ -521,6 +530,7 @@ function off() {
|
|||
Entities.deletingWearable.disconnect(onDeletingWearable);
|
||||
MyAvatar.skeletonModelURLChanged.disconnect(onSkeletonModelURLChanged);
|
||||
MyAvatar.dominantHandChanged.disconnect(onDominantHandChanged);
|
||||
MyAvatar.hmdAvatarAlignmentTypeChanged.disconnect(onHmdAvatarAlignmentTypeChanged);
|
||||
MyAvatar.collisionsEnabledChanged.disconnect(onCollisionsEnabledChanged);
|
||||
MyAvatar.otherAvatarsCollisionsEnabledChanged.disconnect(onOtherAvatarsCollisionsEnabledChanged);
|
||||
MyAvatar.newCollisionSoundURL.disconnect(onNewCollisionSoundUrl);
|
||||
|
@ -542,6 +552,7 @@ function on() {
|
|||
Entities.deletingWearable.connect(onDeletingWearable);
|
||||
MyAvatar.skeletonModelURLChanged.connect(onSkeletonModelURLChanged);
|
||||
MyAvatar.dominantHandChanged.connect(onDominantHandChanged);
|
||||
MyAvatar.hmdAvatarAlignmentTypeChanged.connect(onHmdAvatarAlignmentTypeChanged);
|
||||
MyAvatar.collisionsEnabledChanged.connect(onCollisionsEnabledChanged);
|
||||
MyAvatar.otherAvatarsCollisionsEnabledChanged.connect(onOtherAvatarsCollisionsEnabledChanged);
|
||||
MyAvatar.newCollisionSoundURL.connect(onNewCollisionSoundUrl);
|
||||
|
|
Loading…
Reference in a new issue