6690 lines · 287.4 KB · C++
RawDownload
////  MyAvatar.cpp//  interface/src/avatar////  Created by Mark Peng on 8/16/13.//  Copyright 2012 High Fidelity, Inc.//  Copyright 2020 Vircadia contributors.//  Copyright 2022-2023 Overte e.V.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html//  SPDX-License-Identifier: Apache-2.0// #include "MyAvatar.h" #include <algorithm>#include <vector> #include <QBuffer> #include <glm/gtx/norm.hpp>#include <glm/gtx/vector_angle.hpp> #include <QtCore/QTimer> #include <shared/QtHelpers.h>#include <scripting/HMDScriptingInterface.h>#include <AccountManager.h>#include <AddressManager.h>#include <AnimDebugDraw.h>#include <AnimClip.h>#include <AnimInverseKinematics.h>#include <AudioClient.h>#include <ClientTraitsHandler.h>#include <controllers/UserInputMapper.h>#include <recording/Clip.h>#include <recording/Deck.h>#include <display-plugins/DisplayPlugin.h>#include <recording/Frame.h>#include <FSTReader.h>#include <GeometryUtil.h>#include <GLMHelpers.h>#include <NodeList.h>#include <NetworkingConstants.h>#include <udt/PacketHeaders.h>#include <PathUtils.h>#include <PerfStat.h>#include <ScriptEngine.h>#include <ScriptEngineCast.h>#include <ScriptEngineLogging.h>#include <SharedUtil.h>#include <SoundCache.h>#include <ModelEntityItem.h>#include <TextRenderer3D.h>#include <UserActivityLogger.h>#include <recording/Recorder.h>#include <recording/RecordingScriptingInterface.h>#include <RenderableModelEntityItem.h>#include <VariantMapToScriptValue.h>#include <NetworkingConstants.h> #include "MyHead.h"#include "MySkeletonModel.h"#include "AnimUtil.h"#include "Application.h"#include "AvatarManager.h"#include "AvatarActionHold.h"#include "Menu.h"#include "Util.h"#include "InterfaceLogging.h"#include "DebugDraw.h"#include "EntityEditPacketSender.h"#include "MovingEntitiesOperator.h"#include "SceneScriptingInterface.h"#include "WarningsSuppression.h"#include "ScriptPermissions.h" #include "Application.h" using namespace std; const float DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES = 30.0f; const float YAW_SPEED_DEFAULT = 100.0f;   // degrees/secconst float HMD_YAW_SPEED_DEFAULT = 300.0f;   // degrees/secconst float PITCH_SPEED_DEFAULT = 75.0f; // degrees/sec const float MAX_BOOST_SPEED = 0.5f * DESKTOP_AVATAR_DEFAULT_WALKING_SPEED; // action motor gets additive boost below this speedconst float MIN_AVATAR_SPEED = 0.05f; float MIN_SCRIPTED_MOTOR_TIMESCALE = 0.005f;float DEFAULT_SCRIPTED_MOTOR_TIMESCALE = 1.0e6f;const int SCRIPTED_MOTOR_CAMERA_FRAME = 0;const int SCRIPTED_MOTOR_AVATAR_FRAME = 1;const int SCRIPTED_MOTOR_WORLD_FRAME = 2;const int SCRIPTED_MOTOR_SIMPLE_MODE = 0;const int SCRIPTED_MOTOR_DYNAMIC_MODE = 1;const QString& DEFAULT_AVATAR_COLLISION_SOUND_URL = NetworkingConstants::HF_PUBLIC_CDN_URL + "sounds/Collisions-otherorganic/Body_Hits_Impact.wav"; const float MyAvatar::ZOOM_MIN = 0.5f;const float MyAvatar::ZOOM_MAX = 25.0f;const float MyAvatar::ZOOM_DEFAULT = 1.5f;const float MIN_SCALE_CHANGED_DELTA = 0.001f;const int MODE_READINGS_RING_BUFFER_SIZE = 500;const float CENTIMETERS_PER_METER = 100.0f; const QString AVATAR_SETTINGS_GROUP_NAME { "Avatar" }; static const QString ALLOW_AVATAR_STANDING_ALWAYS = QStringLiteral("Always");static const QString ALLOW_AVATAR_STANDING_WHEN_USER_IS_STANDING = QStringLiteral("UserStanding"); const QString HEAD_BLEND_DIRECTIONAL_ALPHA_NAME = "lookAroundAlpha";const QString HEAD_BLEND_LINEAR_ALPHA_NAME = "lookBlendAlpha";const QString SEATED_HEAD_BLEND_LINEAR_ALPHA_NAME = "seatedLookBlendAlpha"; const QString POINT_REACTION_NAME = "point";const QString POINT_BLEND_DIRECTIONAL_ALPHA_NAME = "pointAroundAlpha";const QString POINT_BLEND_LINEAR_ALPHA_NAME = "pointBlendAlpha";const QString POINT_REF_JOINT_NAME = "RightShoulder";const float POINT_ALPHA_BLENDING = 1.0f; STATIC_SCRIPT_TYPES_INITIALIZER(+[](ScriptManager* manager){    auto scriptEngine = manager->engine();     MyAvatar::registerMetaTypes(scriptEngine);}); STATIC_SCRIPT_INITIALIZER(+[](ScriptManager* manager){    auto scriptEngine = manager->engine();     auto avatarManager = DependencyManager::get<AvatarManager>();    if (avatarManager) {        avatarManager->getMyAvatar()->registerProperties(scriptEngine);    } else {        qWarning(scriptengine) << "Cannot register MyAvatar with script engine, AvatarManager instance not available";    }}); const std::array<QString, static_cast<uint>(MyAvatar::AllowAvatarStandingPreference::Count)>    MyAvatar::allowAvatarStandingPreferenceStrings = {    QStringLiteral("Standing"),    QStringLiteral("Seated"),    QStringLiteral("ForcedHeight")}; const std::array<QString, static_cast<uint>(MyAvatar::AllowAvatarLeaningPreference::Count)>    MyAvatar::allowAvatarLeaningPreferenceStrings = {    QStringLiteral("WhenUserIsStanding"),    QStringLiteral("Always"),    QStringLiteral("Never"),    QStringLiteral("AlwaysNoRecenter")}; MyAvatar::AllowAvatarStandingPreference stringToAllowAvatarStandingPreference(const QString& str) {    for (uint stringIndex = 0; stringIndex < static_cast<uint>(MyAvatar::AllowAvatarStandingPreference::Count); stringIndex++) {        if (MyAvatar::allowAvatarStandingPreferenceStrings[stringIndex] == str) {            return static_cast<MyAvatar::AllowAvatarStandingPreference>(stringIndex);        }    }     return MyAvatar::AllowAvatarStandingPreference::Default;} MyAvatar::AllowAvatarLeaningPreference stringToAllowAvatarLeaningPreference(const QString& str) {    for (uint stringIndex = 0; stringIndex < static_cast<uint>(MyAvatar::AllowAvatarLeaningPreference::Count); stringIndex++) {        if (MyAvatar::allowAvatarLeaningPreferenceStrings[stringIndex] == str) {            return static_cast<MyAvatar::AllowAvatarLeaningPreference>(stringIndex);        }    }     return MyAvatar::AllowAvatarLeaningPreference::Default;} static const QStringList TRIGGER_REACTION_NAMES = {    QString("positive"),    QString("negative")}; static const QStringList BEGIN_END_REACTION_NAMES = {    QString("raiseHand"),    QString("applaud"),    QString("point")}; static int triggerReactionNameToIndex(const QString& reactionName) {    assert(NUM_AVATAR_TRIGGER_REACTIONS == TRIGGER_REACTION_NAMES.size());    return TRIGGER_REACTION_NAMES.indexOf(reactionName);} static int beginEndReactionNameToIndex(const QString& reactionName) {    assert(NUM_AVATAR_BEGIN_END_REACTIONS == BEGIN_END_REACTION_NAMES.size());    return BEGIN_END_REACTION_NAMES.indexOf(reactionName);} MyAvatar::MyAvatar(QThread* thread) :    Avatar(thread),    _yawSpeed(YAW_SPEED_DEFAULT),    _hmdYawSpeed(HMD_YAW_SPEED_DEFAULT),    _pitchSpeed(PITCH_SPEED_DEFAULT),    _scriptedMotorTimescale(DEFAULT_SCRIPTED_MOTOR_TIMESCALE),    _scriptedMotorFrame(SCRIPTED_MOTOR_CAMERA_FRAME),    _scriptedMotorMode(SCRIPTED_MOTOR_SIMPLE_MODE),    _motionBehaviors(AVATAR_MOTION_DEFAULTS),    _characterController(std::shared_ptr<MyAvatar>(this), _follow._timeRemaining),    _eyeContactTarget(LEFT_EYE),    _realWorldFieldOfView("realWorldFieldOfView",                          DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES),    _useAdvancedMovementControls("advancedMovementForHandControllersIsChecked", true),    _showPlayArea("showPlayArea", true),    _smoothOrientationTimer(std::numeric_limits<float>::max()),    _smoothOrientationInitial(),    _smoothOrientationTarget(),    _hmdSensorMatrix(),    _hmdSensorOrientation(),    _hmdSensorPosition(),    _recentModeReadings(MODE_READINGS_RING_BUFFER_SIZE),    _bodySensorMatrix(),    _goToPending(false),    _goToSafe(true),    _goToFeetAjustment(false),    _goToPosition(),    _goToOrientation(),    _prevShouldDrawHead(true),    _audioListenerMode(FROM_HEAD),    _dominantHandSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "dominantHand", DOMINANT_RIGHT_HAND),    _strafeEnabledSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "strafeEnabled", DEFAULT_STRAFE_ENABLED),    _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),    _hmdYawSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "hmdYawSpeed", _hmdYawSpeed),    _cameraSensitivitySetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "cameraSensitivity", qApp->getCamera().getSensitivity()),    _pitchSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "pitchSpeed", _pitchSpeed),    _fullAvatarURLSetting(QStringList() << SETTINGS_FULL_PRIVATE_GROUP_NAME << AVATAR_SETTINGS_GROUP_NAME << "fullAvatarURL",                          AvatarData::defaultFullAvatarModelUrl()),    _fullAvatarModelNameSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "fullAvatarModelName", _fullAvatarModelName),    _animGraphURLSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "animGraphURL", QUrl("")),    _displayNameSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "displayName", ""),    _collisionSoundURLSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "collisionSoundURL", QUrl(_collisionSoundURL)),    _useSnapTurnSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "useSnapTurn", _useSnapTurn),    _hoverWhenUnsupportedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "hoverWhenUnsupported", _hoverWhenUnsupported),    _userHeightSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "userHeight", DEFAULT_AVATAR_HEIGHT),    _flyingHMDSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "flyingHMD", _flyingPrefHMD),    _movementReferenceSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "movementReference", _movementReference),    _avatarEntityCountSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "avatarEntityData" << "size", 0),    _vrWalkSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "vrWalkSpeed", _vrWalkSpeed.get()),    _allowAvatarStandingPreferenceSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "allowAvatarStandingPreference",                                          allowAvatarStandingPreferenceStrings[static_cast<uint>(                                              AllowAvatarStandingPreference::Default)]),    _allowAvatarLeaningPreferenceSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "allowAvatarLeaningPreference",                                         allowAvatarLeaningPreferenceStrings[static_cast<uint>(                                             AllowAvatarLeaningPreference::Default)]) {    _clientTraitsHandler.reset(new ClientTraitsHandler(this));     // give the pointer to our head to inherited _headData variable from AvatarData    _headData = new MyHead(this);     _skeletonModel = std::make_shared<MySkeletonModel>(this, nullptr);    _skeletonModel->setLoadingPriorityOperator([]() { return MYAVATAR_LOADING_PRIORITY; });    connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished);    connect(_skeletonModel.get(), &Model::setURLFinished, this, [this](bool success) {        if (success) {            qApp->unloadAvatarScripts();            _shouldLoadScripts = true;        }    });    connect(_skeletonModel.get(), &Model::rigReady, this, [this]() {        if (_shouldLoadScripts) {            auto hfmModel = getSkeletonModel()->getHFMModel();            qApp->loadAvatarScripts(hfmModel.scripts);            _shouldLoadScripts = false;        }    });    connect(_skeletonModel.get(), &Model::rigReady, this, &Avatar::rigReady);    connect(_skeletonModel.get(), &Model::rigReset, this, &Avatar::rigReset);    connect(&_skeletonModel->getRig(), &Rig::onLoadComplete, this, &MyAvatar::updateCollisionCapsuleCache);    connect(this, &MyAvatar::sensorToWorldScaleChanged, this, &MyAvatar::updateCollisionCapsuleCache);    using namespace recording;    _skeletonModel->flagAsCauterized();     clearDriveKeys();     // Necessary to select the correct slot    using SlotType = void(MyAvatar::*)(const glm::vec3&, bool, const glm::quat&, bool);     // connect to AddressManager signal for location jumps    connect(DependencyManager::get<AddressManager>().data(), &AddressManager::locationChangeRequired,            this, static_cast<SlotType>(&MyAvatar::goToFeetLocation));     // handle scale constraints imposed on us by the domain-server    auto& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();     // when we connect to a domain and retrieve its settings, we restrict our max/min scale based on those settings    connect(&domainHandler, &DomainHandler::settingsReceived, this, &MyAvatar::restrictScaleFromDomainSettings);     // when we leave a domain we lift whatever restrictions that domain may have placed on our scale    connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &MyAvatar::leaveDomain);     auto nodeList = DependencyManager::get<NodeList>();    connect(nodeList.data(), &NodeList::canRezAvatarEntitiesChanged, this, &MyAvatar::handleCanRezAvatarEntitiesChanged);     _bodySensorMatrix = deriveBodyFromHMDSensor();     using namespace recording;     auto player = DependencyManager::get<Deck>();    auto recorder = DependencyManager::get<Recorder>();    connect(player.data(), &Deck::playbackStateChanged, [=, this] {        bool isPlaying = player->isPlaying();        if (isPlaying) {            auto recordingInterface = DependencyManager::get<RecordingScriptingInterface>();            if (recordingInterface->getPlayFromCurrentLocation()) {                setRecordingBasis();            }            _previousCollisionMask = _characterController.computeCollisionMask();            _characterController.setCollisionless(true);        } else {            clearRecordingBasis();            useFullAvatarURL(_fullAvatarURLFromPreferences, _fullAvatarModelName);            if (_previousCollisionMask != BULLET_COLLISION_MASK_COLLISIONLESS) {                _characterController.setCollisionless(false);            }        }         auto audioIO = DependencyManager::get<AudioClient>();        audioIO->setIsPlayingBackRecording(isPlaying);         _skeletonModel->getRig().setEnableAnimations(!isPlaying);    });     connect(recorder.data(), &Recorder::recordingStateChanged, [=, this] {        if (recorder->isRecording()) {            createRecordingIDs();            setRecordingBasis();        } else {            clearRecordingBasis();        }    });     static const recording::FrameType AVATAR_FRAME_TYPE = recording::Frame::registerFrameType(AvatarData::FRAME_NAME);    Frame::registerFrameHandler(AVATAR_FRAME_TYPE, [=, this](Frame::ConstPointer frame) {        static AvatarData dummyAvatar;        AvatarData::fromFrame(frame->data, dummyAvatar);        if (getRecordingBasis()) {            dummyAvatar.setRecordingBasis(getRecordingBasis());        } else {            dummyAvatar.clearRecordingBasis();        }         auto recordingInterface = DependencyManager::get<RecordingScriptingInterface>();         if (recordingInterface->getPlayerUseSkeletonModel() && dummyAvatar.getSkeletonModelURL().isValid() &&            (dummyAvatar.getSkeletonModelURL() != getSkeletonModelURL())) {             setSkeletonModelURL(dummyAvatar.getSkeletonModelURL());        }         if (recordingInterface->getPlayerUseDisplayName() && dummyAvatar.getDisplayName() != getDisplayName()) {            setDisplayName(dummyAvatar.getDisplayName());        }         setWorldPosition(dummyAvatar.getWorldPosition());        setWorldOrientation(dummyAvatar.getWorldOrientation());         auto headData = dummyAvatar.getHeadData();        if (headData && _headData) {            // blendshapes            if (!headData->getBlendshapeCoefficients().isEmpty()) {                _headData->setBlendshapeCoefficients(headData->getBlendshapeCoefficients());            }            // head orientation            _headData->setLookAtPosition(headData->getLookAtPosition());        }         auto jointData = dummyAvatar.getRawJointData();        if (jointData.length() > 0) {            _skeletonModel->getRig().copyJointsFromJointData(jointData);        }    });     connect(&(_skeletonModel->getRig()), &Rig::onLoadComplete, this, &MyAvatar::onLoadComplete);    connect(&(_skeletonModel->getRig()), &Rig::onLoadFailed, this, &MyAvatar::onLoadFailed);     _characterController.setDensity(_density);     _addAvatarEntitiesToTreeTimer.setSingleShot(true);    connect(&_addAvatarEntitiesToTreeTimer, &QTimer::timeout, [this] {        addAvatarEntitiesToTree();    });} MyAvatar::~MyAvatar() {    _lookAtTargetAvatar.reset();    if (_addAvatarEntitiesToTreeTimer.isActive()) {        _addAvatarEntitiesToTreeTimer.stop();    }} QString MyAvatar::getDominantHand() const {    return _dominantHand.get();} void MyAvatar::setStrafeEnabled(bool enabled) {    _strafeEnabled.set(enabled);} bool MyAvatar::getStrafeEnabled() const {    return _strafeEnabled.get();} void MyAvatar::setDominantHand(const QString& hand) {    if (hand == DOMINANT_LEFT_HAND || hand == DOMINANT_RIGHT_HAND) {        bool changed = (hand != _dominantHand.get());        if (changed) {            _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);    }} void MyAvatar::requestDisableHandTouch() {    std::lock_guard<std::mutex> guard(_disableHandTouchMutex);    _disableHandTouchCount++;    emit shouldDisableHandTouchChanged(_disableHandTouchCount > 0);} void MyAvatar::requestEnableHandTouch() {    std::lock_guard<std::mutex> guard(_disableHandTouchMutex);    _disableHandTouchCount = std::max(_disableHandTouchCount - 1, 0);    emit shouldDisableHandTouchChanged(_disableHandTouchCount > 0);} void MyAvatar::disableHandTouchForID(const QUuid& entityID) {    emit disableHandTouchForIDChanged(entityID, true);} void MyAvatar::enableHandTouchForID(const QUuid& entityID) {    emit disableHandTouchForIDChanged(entityID, false);} void MyAvatar::registerMetaTypes(ScriptEnginePointer engine) {    scriptRegisterMetaType<AudioListenerMode, audioListenModeToScriptValue, audioListenModeFromScriptValue>(engine.get());    scriptRegisterMetaType<MyAvatar::DriveKeys, driveKeysToScriptValue, driveKeysFromScriptValue>(engine.get(), "DriveKeys");    qDebug() << "MyAvatar::registerMetaTypes";} void MyAvatar::registerProperties(ScriptEnginePointer engine) {    ScriptValue value = engine->newQObject(this, ScriptEngine::QtOwnership);    engine->globalObject().setProperty("MyAvatar", value);     ScriptValue driveKeys = engine->newObject();    auto metaEnum = QMetaEnum::fromType<DriveKeys>();    for (int i = 0; i < MAX_DRIVE_KEYS; ++i) {        driveKeys.setProperty(metaEnum.key(i), metaEnum.value(i));    }    engine->globalObject().setProperty("DriveKeys", driveKeys);}  void MyAvatar::setOrientationVar(const QVariant& newOrientationVar) {    Avatar::setWorldOrientation(quatFromVariant(newOrientationVar));} QVariant MyAvatar::getOrientationVar() const {    return quatToVariant(Avatar::getWorldOrientation());} glm::quat MyAvatar::getOrientationOutbound() const {    // Allows MyAvatar to send out smoothed data to remote agents if required.    if (_smoothOrientationTimer > SMOOTH_TIME_ORIENTATION) {        return (getLocalOrientation());    }     // Smooth the remote avatar movement.    float t = _smoothOrientationTimer / SMOOTH_TIME_ORIENTATION;    float interp = Interpolate::easeInOutQuad(glm::clamp(t, 0.0f, 1.0f));    return (slerp(_smoothOrientationInitial, _smoothOrientationTarget, interp));} QByteArray MyAvatar::toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking) {    CameraMode mode = qApp->getCamera().getMode();    _globalPosition = getWorldPosition();    // This might not be right! Isn't the capsule local offset in avatar space, and don't we need to add the radius to the y as well? -HRS 5/26/17    _globalBoundingBoxDimensions.x = _characterController.getCapsuleRadius();    _globalBoundingBoxDimensions.y = _characterController.getCapsuleHalfHeight();    _globalBoundingBoxDimensions.z = _characterController.getCapsuleRadius();    _globalBoundingBoxOffset = _characterController.getCapsuleLocalOffset();    if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT || mode == CAMERA_MODE_LOOK_AT || mode == CAMERA_MODE_SELFIE) {

Lines 1–500 of 6690.