//// 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. Load more
// // 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/sec const float HMD_YAW_SPEED_DEFAULT = 300.0f; // degrees/sec const 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 speed const 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) {