//// MyAvatar.cpp// interface/src/avatar//// Created by Mark Peng on 8/16/13.// Copyright 2012 High Fidelity, Inc.//// Distributed under the Apache License, Version 2.0.// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include <algorithm>#include <vector> #include <QBuffer> #include <glm/gtx/norm.hpp>#include <glm/gtx/vector_angle.hpp> #include <QtCore/QTimer> #include <scripting/HMDScriptingInterface.h>#include <AccountManager.h>#include <AddressManager.h>#include <AudioClient.h>#include <display-plugins/DisplayPlugin.h>#include <FSTReader.h>#include <GeometryUtil.h>#include <NodeList.h>#include <udt/PacketHeaders.h>#include <PathUtils.h>#include <PerfStat.h>#include <SharedUtil.h>#include <SoundCache.h>#include <TextRenderer3D.h>#include <UserActivityLogger.h>#include <AnimDebugDraw.h>#include <AnimClip.h>#include <recording/Deck.h>#include <recording/Recorder.h>#include <recording/Clip.h>#include <recording/Frame.h>#include <RecordingScriptingInterface.h> #include "Application.h"#include "devices/Faceshift.h"#include "AvatarManager.h"#include "Menu.h"#include "MyAvatar.h"#include "Physics.h"#include "Util.h"#include "InterfaceLogging.h"#include "DebugDraw.h"#include "EntityEditPacketSender.h"#include "MovingEntitiesOperator.h" using namespace std; const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f);const float DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES = 30.0f; const float MAX_WALKING_SPEED = 2.6f; // human walking speedconst float MAX_BOOST_SPEED = 0.5f * MAX_WALKING_SPEED; // action motor gets additive boost below this speedconst float MIN_AVATAR_SPEED = 0.05f;const float MIN_AVATAR_SPEED_SQUARED = MIN_AVATAR_SPEED * MIN_AVATAR_SPEED; // speed is set to zero below this const float YAW_SPEED_DEFAULT = 120.0f; // degrees/secconst float PITCH_SPEED_DEFAULT = 90.0f; // degrees/sec // TODO: normalize avatar speed for standard avatar size, then scale all motion logic// to properly follow avatar size.float MAX_AVATAR_SPEED = 30.0f;float MAX_ACTION_MOTOR_SPEED = MAX_AVATAR_SPEED;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 QString& DEFAULT_AVATAR_COLLISION_SOUND_URL = "https://hifi-public.s3.amazonaws.com/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; MyAvatar::MyAvatar(RigPointer rig) : Avatar(rig), _wasPushing(false), _isPushing(false), _isBeingPushed(false), _isBraking(false), _boomLength(ZOOM_DEFAULT), _yawSpeed(YAW_SPEED_DEFAULT), _pitchSpeed(PITCH_SPEED_DEFAULT), _thrust(0.0f), _actionMotorVelocity(0.0f), _scriptedMotorVelocity(0.0f), _scriptedMotorTimescale(DEFAULT_SCRIPTED_MOTOR_TIMESCALE), _scriptedMotorFrame(SCRIPTED_MOTOR_CAMERA_FRAME), _motionBehaviors(AVATAR_MOTION_DEFAULTS), _characterController(this), _lookAtTargetAvatar(), _shouldRender(true), _eyeContactTarget(LEFT_EYE), _realWorldFieldOfView("realWorldFieldOfView", DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES), _hmdSensorMatrix(), _hmdSensorOrientation(), _hmdSensorPosition(), _bodySensorMatrix(), _sensorToWorldMatrix(), _goToPending(false), _goToPosition(), _goToOrientation(), _rig(rig), _prevShouldDrawHead(true), _audioListenerMode(FROM_HEAD), _hmdAtRestDetector(glm::vec3(0), glm::quat()){ using namespace recording; for (int i = 0; i < MAX_DRIVE_KEYS; i++) { _driveKeys[i] = 0.0f; } // connect to AddressManager signal for location jumps connect(DependencyManager::get<AddressManager>().data(), &AddressManager::locationChangeRequired, [=](const glm::vec3& newPosition, bool hasOrientation, const glm::quat& newOrientation, bool shouldFaceLocation){ goToLocation(newPosition, hasOrientation, newOrientation, shouldFaceLocation); }); _characterController.setEnabled(true); _bodySensorMatrix = deriveBodyFromHMDSensor(); using namespace recording; auto player = DependencyManager::get<Deck>(); auto recorder = DependencyManager::get<Recorder>(); connect(player.data(), &Deck::playbackStateChanged, [=] { if (player->isPlaying()) { auto recordingInterface = DependencyManager::get<RecordingScriptingInterface>(); if (recordingInterface->getPlayFromCurrentLocation()) { setRecordingBasis(); } } else { clearRecordingBasis(); } }); connect(recorder.data(), &Recorder::recordingStateChanged, [=] { if (recorder->isRecording()) { setRecordingBasis(); } else { clearRecordingBasis(); } }); static const recording::FrameType AVATAR_FRAME_TYPE = recording::Frame::registerFrameType(AvatarData::FRAME_NAME); Frame::registerFrameHandler(AVATAR_FRAME_TYPE, [=](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())) { // FIXME //myAvatar->useFullAvatarURL() } if (recordingInterface->getPlayerUseDisplayName() && dummyAvatar.getDisplayName() != getDisplayName()) { setDisplayName(dummyAvatar.getDisplayName()); } setPosition(dummyAvatar.getPosition()); setOrientation(dummyAvatar.getOrientation()); if (!dummyAvatar.getAttachmentData().isEmpty()) { setAttachmentData(dummyAvatar.getAttachmentData()); } auto headData = dummyAvatar.getHeadData(); if (headData && _headData) { // blendshapes if (!headData->getBlendshapeCoefficients().isEmpty()) { _headData->setBlendshapeCoefficients(headData->getBlendshapeCoefficients()); } // head orientation _headData->setLookAtPosition(headData->getLookAtPosition()); } }); connect(rig.get(), SIGNAL(onLoadComplete()), this, SIGNAL(onLoadComplete()));} MyAvatar::~MyAvatar() { _lookAtTargetAvatar.reset();} void MyAvatar::setOrientationVar(const QVariant& newOrientationVar) { Avatar::setOrientation(quatFromVariant(newOrientationVar));} QVariant MyAvatar::getOrientationVar() const { return quatToVariant(Avatar::getOrientation());} // virtualvoid MyAvatar::simulateAttachments(float deltaTime) { // don't update attachments here, do it in harvestResultsFromPhysicsSimulation()} QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) { CameraMode mode = qApp->getCamera()->getMode(); _globalPosition = getPosition(); if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) { // fake the avatar position that is sent up to the AvatarMixer glm::vec3 oldPosition = getPosition(); setPosition(getSkeletonPosition()); QByteArray array = AvatarData::toByteArray(cullSmallChanges, sendAll); // copy the correct position back setPosition(oldPosition); return array; } return AvatarData::toByteArray(cullSmallChanges, sendAll);} void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "reset", Q_ARG(bool, andRecenter), Q_ARG(bool, andReload), Q_ARG(bool, andHead)); return; } // Reset dynamic state. _wasPushing = _isPushing = _isBraking = false; _follow.deactivate(); if (andReload) { _skeletonModel->reset(); } if (andHead) { // which drives camera in desktop getHead()->reset(); } setThrust(glm::vec3(0.0f)); if (andRecenter) { // derive the desired body orientation from the *old* hmd orientation, before the sensor reset. auto newBodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. // transform this body into world space auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix; auto worldBodyPos = extractTranslation(worldBodyMatrix); auto worldBodyRot = glm::normalize(glm::quat_cast(worldBodyMatrix)); // this will become our new position. setPosition(worldBodyPos); setOrientation(worldBodyRot); // now sample the new hmd orientation AFTER sensor reset, which should be identity. glm::mat4 identity; updateFromHMDSensorMatrix(identity); // update the body in sensor space using the new hmd sensor sample _bodySensorMatrix = deriveBodyFromHMDSensor(); // rebuild the sensor to world matrix such that, the HMD will point in the desired orientation. // i.e. the along avatar's current position and orientation. updateSensorToWorldMatrix(); }} void MyAvatar::update(float deltaTime) { // update moving average of HMD facing in xz plane. const float HMD_FACING_TIMESCALE = 4.0f; // very slow average float tau = deltaTime / HMD_FACING_TIMESCALE; _hmdSensorFacingMovingAverage = lerp(_hmdSensorFacingMovingAverage, _hmdSensorFacing, tau); #ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE glm::vec3 p = transformPoint(getSensorToWorldMatrix(), _hmdSensorPosition + glm::vec3(_hmdSensorFacingMovingAverage.x, 0.0f, _hmdSensorFacingMovingAverage.y)); DebugDraw::getInstance().addMarker("facing-avg", getOrientation(), p, glm::vec4(1.0f)); p = transformPoint(getSensorToWorldMatrix(), _hmdSensorPosition + glm::vec3(_hmdSensorFacing.x, 0.0f, _hmdSensorFacing.y)); DebugDraw::getInstance().addMarker("facing", getOrientation(), p, glm::vec4(1.0f));#endif if (_goToPending) { setPosition(_goToPosition); setOrientation(_goToOrientation); _hmdSensorFacingMovingAverage = _hmdSensorFacing; // reset moving average _goToPending = false; // updateFromHMDSensorMatrix (called from paintGL) expects that the sensorToWorldMatrix is updated for any position changes // that happen between render and Application::update (which calls updateSensorToWorldMatrix to do so). // However, render/MyAvatar::update/Application::update don't always match (e.g., when using the separate avatar update thread), // so we update now. It's ok if it updates again in the normal way. updateSensorToWorldMatrix(); emit positionGoneTo(); } Head* head = getHead(); head->relax(deltaTime); updateFromTrackers(deltaTime); // Get audio loudness data from audio input device auto audio = DependencyManager::get<AudioClient>(); head->setAudioLoudness(audio->getLastInputLoudness()); head->setAudioAverageLoudness(audio->getAudioAverageInputLoudness()); if (_avatarEntityDataLocallyEdited) { sendIdentityPacket(); } simulate(deltaTime); currentEnergy += energyChargeRate; currentEnergy -= getAccelerationEnergy(); currentEnergy -= getAudioEnergy(); if(didTeleport()) { currentEnergy = 0.0f; } currentEnergy = max(0.0f, min(currentEnergy,1.0f)); emit energyChanged(currentEnergy); updateEyeContactTarget(deltaTime);} void MyAvatar::updateEyeContactTarget(float deltaTime) { _eyeContactTargetTimer -= deltaTime; if (_eyeContactTargetTimer < 0.0f) { const float CHANCE_OF_CHANGING_TARGET = 0.01f; if (randFloat() < CHANCE_OF_CHANGING_TARGET) { float const FIFTY_FIFTY_CHANCE = 0.5f; float const EYE_TO_MOUTH_CHANCE = 0.25f; switch (_eyeContactTarget) { case LEFT_EYE: _eyeContactTarget = (randFloat() < EYE_TO_MOUTH_CHANCE) ? MOUTH : RIGHT_EYE; break; case RIGHT_EYE: _eyeContactTarget = (randFloat() < EYE_TO_MOUTH_CHANCE) ? MOUTH : LEFT_EYE; break; case MOUTH: default: _eyeContactTarget = (randFloat() < FIFTY_FIFTY_CHANCE) ? RIGHT_EYE : LEFT_EYE; break; } const float EYE_TARGET_DELAY_TIME = 0.33f; _eyeContactTargetTimer = EYE_TARGET_DELAY_TIME; } }} extern QByteArray avatarStateToFrame(const AvatarData* _avatar);extern void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar); void MyAvatar::simulate(float deltaTime) { PerformanceTimer perfTimer("simulate"); animateScaleChanges(deltaTime); { PerformanceTimer perfTimer("transform"); bool stepAction = false; // When there are no step values, we zero out the last step pulse. // This allows a user to do faster snapping by tapping a control for (int i = STEP_TRANSLATE_X; !stepAction && i <= STEP_YAW; ++i) { if (_driveKeys[i] != 0.0f) { stepAction = true; } } updateOrientation(deltaTime); updatePosition(deltaTime); } // update sensorToWorldMatrix for camera and hand controllers // before we perform rig animations and IK. updateSensorToWorldMatrix(); { PerformanceTimer perfTimer("skeleton"); _skeletonModel->simulate(deltaTime); } // we've achived our final adjusted position and rotation for the avatar // and all of its joints, now update our attachements. Avatar::simulateAttachments(deltaTime); if (!_skeletonModel->hasSkeleton()) { // All the simulation that can be done has been done return; } { PerformanceTimer perfTimer("joints"); // copy out the skeleton joints from the model _rig->copyJointsIntoJointData(_jointData); } { PerformanceTimer perfTimer("head"); Head* head = getHead(); glm::vec3 headPosition; if (!_skeletonModel->getHeadPosition(headPosition)) { headPosition = getPosition(); } head->setPosition(headPosition); head->setScale(getUniformScale()); head->simulate(deltaTime, true); } // Record avatars movements. auto recorder = DependencyManager::get<recording::Recorder>(); if (recorder->isRecording()) { static const recording::FrameType FRAME_TYPE = recording::Frame::registerFrameType(AvatarData::FRAME_NAME); recorder->recordFrame(FRAME_TYPE, toFrame(*this)); } locationChanged(); // if a entity-child of this avatar has moved outside of its queryAACube, update the cube and tell the entity server. EntityTreeRenderer* entityTreeRenderer = qApp->getEntities(); EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr; if (entityTree) { bool flyingAllowed = true; bool ghostingAllowed = true; entityTree->withWriteLock([&] { std::shared_ptr<ZoneEntityItem> zone = entityTreeRenderer->myAvatarZone(); if (zone) { flyingAllowed = zone->getFlyingAllowed(); ghostingAllowed = zone->getGhostingAllowed(); } auto now = usecTimestampNow(); EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); MovingEntitiesOperator moveOperator(entityTree); forEachDescendant([&](SpatiallyNestablePointer object) { // if the queryBox has changed, tell the entity-server if (object->computePuffedQueryAACube() && object->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast<EntityItem>(object); bool success; AACube newCube = entity->getQueryAACube(success); if (success) { moveOperator.addEntityToMoveList(entity, newCube); } if (packetSender) { EntityItemProperties properties = entity->getProperties(); properties.setQueryAACubeDirty(); properties.setLastEdited(now); packetSender->queueEditEntityMessage(PacketType::EntityEdit, entityTree, entity->getID(), properties); entity->setLastBroadcast(usecTimestampNow()); } } }); // also update the position of children in our local octree if (moveOperator.hasMovingEntities()) { PerformanceTimer perfTimer("recurseTreeWithOperator"); entityTree->recurseTreeWithOperator(&moveOperator); } }); _characterController.setFlyingAllowed(flyingAllowed); if (!_characterController.isEnabled() && !ghostingAllowed) { _characterController.setEnabled(true); } } updateAvatarEntities();} // thread-safeglm::mat4 MyAvatar::getSensorToWorldMatrix() const { return _sensorToWorldMatrixCache.get();} // Pass a recent sample of the HMD to the avatar.// This can also update the avatar's position to follow the HMD// as it moves through the world.void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { // update the sensorMatrices based on the new hmd pose _hmdSensorMatrix = hmdSensorMatrix; _hmdSensorPosition = extractTranslation(hmdSensorMatrix); _hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix); _hmdSensorFacing = getFacingDir2D(_hmdSensorOrientation);} // best called at end of main loop, after physics.// update sensor to world matrix from current body position and hmd sensor.// This is so the correct camera can be used for rendering.void MyAvatar::updateSensorToWorldMatrix() { // update the sensor mat so that the body position will end up in the desired // position when driven from the head.Lines 1–500 of 2139. Load more
// // MyAvatar.cpp // interface/src/avatar // // Created by Mark Peng on 8/16/13. // Copyright 2012 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include <algorithm> #include <vector> #include <QBuffer> #include <glm/gtx/norm.hpp> #include <glm/gtx/vector_angle.hpp> #include <QtCore/QTimer> #include <scripting/HMDScriptingInterface.h> #include <AccountManager.h> #include <AddressManager.h> #include <AudioClient.h> #include <display-plugins/DisplayPlugin.h> #include <FSTReader.h> #include <GeometryUtil.h> #include <NodeList.h> #include <udt/PacketHeaders.h> #include <PathUtils.h> #include <PerfStat.h> #include <SharedUtil.h> #include <SoundCache.h> #include <TextRenderer3D.h> #include <UserActivityLogger.h> #include <AnimDebugDraw.h> #include <AnimClip.h> #include <recording/Deck.h> #include <recording/Recorder.h> #include <recording/Clip.h> #include <recording/Frame.h> #include <RecordingScriptingInterface.h> #include "Application.h" #include "devices/Faceshift.h" #include "AvatarManager.h" #include "Menu.h" #include "MyAvatar.h" #include "Physics.h" #include "Util.h" #include "InterfaceLogging.h" #include "DebugDraw.h" #include "EntityEditPacketSender.h" #include "MovingEntitiesOperator.h" using namespace std; const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f); const float DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES = 30.0f; const float MAX_WALKING_SPEED = 2.6f; // human walking speed const float MAX_BOOST_SPEED = 0.5f * MAX_WALKING_SPEED; // action motor gets additive boost below this speed const float MIN_AVATAR_SPEED = 0.05f; const float MIN_AVATAR_SPEED_SQUARED = MIN_AVATAR_SPEED * MIN_AVATAR_SPEED; // speed is set to zero below this const float YAW_SPEED_DEFAULT = 120.0f; // degrees/sec const float PITCH_SPEED_DEFAULT = 90.0f; // degrees/sec // TODO: normalize avatar speed for standard avatar size, then scale all motion logic // to properly follow avatar size. float MAX_AVATAR_SPEED = 30.0f; float MAX_ACTION_MOTOR_SPEED = MAX_AVATAR_SPEED; 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 QString& DEFAULT_AVATAR_COLLISION_SOUND_URL = "https://hifi-public.s3.amazonaws.com/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; MyAvatar::MyAvatar(RigPointer rig) : Avatar(rig), _wasPushing(false), _isPushing(false), _isBeingPushed(false), _isBraking(false), _boomLength(ZOOM_DEFAULT), _yawSpeed(YAW_SPEED_DEFAULT), _pitchSpeed(PITCH_SPEED_DEFAULT), _thrust(0.0f), _actionMotorVelocity(0.0f), _scriptedMotorVelocity(0.0f), _scriptedMotorTimescale(DEFAULT_SCRIPTED_MOTOR_TIMESCALE), _scriptedMotorFrame(SCRIPTED_MOTOR_CAMERA_FRAME), _motionBehaviors(AVATAR_MOTION_DEFAULTS), _characterController(this), _lookAtTargetAvatar(), _shouldRender(true), _eyeContactTarget(LEFT_EYE), _realWorldFieldOfView("realWorldFieldOfView", DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES), _hmdSensorMatrix(), _hmdSensorOrientation(), _hmdSensorPosition(), _bodySensorMatrix(), _sensorToWorldMatrix(), _goToPending(false), _goToPosition(), _goToOrientation(), _rig(rig), _prevShouldDrawHead(true), _audioListenerMode(FROM_HEAD), _hmdAtRestDetector(glm::vec3(0), glm::quat()) { using namespace recording; for (int i = 0; i < MAX_DRIVE_KEYS; i++) { _driveKeys[i] = 0.0f; } // connect to AddressManager signal for location jumps connect(DependencyManager::get<AddressManager>().data(), &AddressManager::locationChangeRequired, [=](const glm::vec3& newPosition, bool hasOrientation, const glm::quat& newOrientation, bool shouldFaceLocation){ goToLocation(newPosition, hasOrientation, newOrientation, shouldFaceLocation); }); _characterController.setEnabled(true); _bodySensorMatrix = deriveBodyFromHMDSensor(); using namespace recording; auto player = DependencyManager::get<Deck>(); auto recorder = DependencyManager::get<Recorder>(); connect(player.data(), &Deck::playbackStateChanged, [=] { if (player->isPlaying()) { auto recordingInterface = DependencyManager::get<RecordingScriptingInterface>(); if (recordingInterface->getPlayFromCurrentLocation()) { setRecordingBasis(); } } else { clearRecordingBasis(); } }); connect(recorder.data(), &Recorder::recordingStateChanged, [=] { if (recorder->isRecording()) { setRecordingBasis(); } else { clearRecordingBasis(); } }); static const recording::FrameType AVATAR_FRAME_TYPE = recording::Frame::registerFrameType(AvatarData::FRAME_NAME); Frame::registerFrameHandler(AVATAR_FRAME_TYPE, [=](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())) { // FIXME //myAvatar->useFullAvatarURL() } if (recordingInterface->getPlayerUseDisplayName() && dummyAvatar.getDisplayName() != getDisplayName()) { setDisplayName(dummyAvatar.getDisplayName()); } setPosition(dummyAvatar.getPosition()); setOrientation(dummyAvatar.getOrientation()); if (!dummyAvatar.getAttachmentData().isEmpty()) { setAttachmentData(dummyAvatar.getAttachmentData()); } auto headData = dummyAvatar.getHeadData(); if (headData && _headData) { // blendshapes if (!headData->getBlendshapeCoefficients().isEmpty()) { _headData->setBlendshapeCoefficients(headData->getBlendshapeCoefficients()); } // head orientation _headData->setLookAtPosition(headData->getLookAtPosition()); } }); connect(rig.get(), SIGNAL(onLoadComplete()), this, SIGNAL(onLoadComplete())); } MyAvatar::~MyAvatar() { _lookAtTargetAvatar.reset(); } void MyAvatar::setOrientationVar(const QVariant& newOrientationVar) { Avatar::setOrientation(quatFromVariant(newOrientationVar)); } QVariant MyAvatar::getOrientationVar() const { return quatToVariant(Avatar::getOrientation()); } // virtual void MyAvatar::simulateAttachments(float deltaTime) { // don't update attachments here, do it in harvestResultsFromPhysicsSimulation() } QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) { CameraMode mode = qApp->getCamera()->getMode(); _globalPosition = getPosition(); if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) { // fake the avatar position that is sent up to the AvatarMixer glm::vec3 oldPosition = getPosition(); setPosition(getSkeletonPosition()); QByteArray array = AvatarData::toByteArray(cullSmallChanges, sendAll); // copy the correct position back setPosition(oldPosition); return array; } return AvatarData::toByteArray(cullSmallChanges, sendAll); } void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "reset", Q_ARG(bool, andRecenter), Q_ARG(bool, andReload), Q_ARG(bool, andHead)); return; } // Reset dynamic state. _wasPushing = _isPushing = _isBraking = false; _follow.deactivate(); if (andReload) { _skeletonModel->reset(); } if (andHead) { // which drives camera in desktop getHead()->reset(); } setThrust(glm::vec3(0.0f)); if (andRecenter) { // derive the desired body orientation from the *old* hmd orientation, before the sensor reset. auto newBodySensorMatrix = deriveBodyFromHMDSensor(); // Based on current cached HMD position/rotation.. // transform this body into world space auto worldBodyMatrix = _sensorToWorldMatrix * newBodySensorMatrix; auto worldBodyPos = extractTranslation(worldBodyMatrix); auto worldBodyRot = glm::normalize(glm::quat_cast(worldBodyMatrix)); // this will become our new position. setPosition(worldBodyPos); setOrientation(worldBodyRot); // now sample the new hmd orientation AFTER sensor reset, which should be identity. glm::mat4 identity; updateFromHMDSensorMatrix(identity); // update the body in sensor space using the new hmd sensor sample _bodySensorMatrix = deriveBodyFromHMDSensor(); // rebuild the sensor to world matrix such that, the HMD will point in the desired orientation. // i.e. the along avatar's current position and orientation. updateSensorToWorldMatrix(); } } void MyAvatar::update(float deltaTime) { // update moving average of HMD facing in xz plane. const float HMD_FACING_TIMESCALE = 4.0f; // very slow average float tau = deltaTime / HMD_FACING_TIMESCALE; _hmdSensorFacingMovingAverage = lerp(_hmdSensorFacingMovingAverage, _hmdSensorFacing, tau); #ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE glm::vec3 p = transformPoint(getSensorToWorldMatrix(), _hmdSensorPosition + glm::vec3(_hmdSensorFacingMovingAverage.x, 0.0f, _hmdSensorFacingMovingAverage.y)); DebugDraw::getInstance().addMarker("facing-avg", getOrientation(), p, glm::vec4(1.0f)); p = transformPoint(getSensorToWorldMatrix(), _hmdSensorPosition + glm::vec3(_hmdSensorFacing.x, 0.0f, _hmdSensorFacing.y)); DebugDraw::getInstance().addMarker("facing", getOrientation(), p, glm::vec4(1.0f)); #endif if (_goToPending) { setPosition(_goToPosition); setOrientation(_goToOrientation); _hmdSensorFacingMovingAverage = _hmdSensorFacing; // reset moving average _goToPending = false; // updateFromHMDSensorMatrix (called from paintGL) expects that the sensorToWorldMatrix is updated for any position changes // that happen between render and Application::update (which calls updateSensorToWorldMatrix to do so). // However, render/MyAvatar::update/Application::update don't always match (e.g., when using the separate avatar update thread), // so we update now. It's ok if it updates again in the normal way. updateSensorToWorldMatrix(); emit positionGoneTo(); } Head* head = getHead(); head->relax(deltaTime); updateFromTrackers(deltaTime); // Get audio loudness data from audio input device auto audio = DependencyManager::get<AudioClient>(); head->setAudioLoudness(audio->getLastInputLoudness()); head->setAudioAverageLoudness(audio->getAudioAverageInputLoudness()); if (_avatarEntityDataLocallyEdited) { sendIdentityPacket(); } simulate(deltaTime); currentEnergy += energyChargeRate; currentEnergy -= getAccelerationEnergy(); currentEnergy -= getAudioEnergy(); if(didTeleport()) { currentEnergy = 0.0f; } currentEnergy = max(0.0f, min(currentEnergy,1.0f)); emit energyChanged(currentEnergy); updateEyeContactTarget(deltaTime); } void MyAvatar::updateEyeContactTarget(float deltaTime) { _eyeContactTargetTimer -= deltaTime; if (_eyeContactTargetTimer < 0.0f) { const float CHANCE_OF_CHANGING_TARGET = 0.01f; if (randFloat() < CHANCE_OF_CHANGING_TARGET) { float const FIFTY_FIFTY_CHANCE = 0.5f; float const EYE_TO_MOUTH_CHANCE = 0.25f; switch (_eyeContactTarget) { case LEFT_EYE: _eyeContactTarget = (randFloat() < EYE_TO_MOUTH_CHANCE) ? MOUTH : RIGHT_EYE; break; case RIGHT_EYE: _eyeContactTarget = (randFloat() < EYE_TO_MOUTH_CHANCE) ? MOUTH : LEFT_EYE; break; case MOUTH: default: _eyeContactTarget = (randFloat() < FIFTY_FIFTY_CHANCE) ? RIGHT_EYE : LEFT_EYE; break; } const float EYE_TARGET_DELAY_TIME = 0.33f; _eyeContactTargetTimer = EYE_TARGET_DELAY_TIME; } } } extern QByteArray avatarStateToFrame(const AvatarData* _avatar); extern void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar); void MyAvatar::simulate(float deltaTime) { PerformanceTimer perfTimer("simulate"); animateScaleChanges(deltaTime); { PerformanceTimer perfTimer("transform"); bool stepAction = false; // When there are no step values, we zero out the last step pulse. // This allows a user to do faster snapping by tapping a control for (int i = STEP_TRANSLATE_X; !stepAction && i <= STEP_YAW; ++i) { if (_driveKeys[i] != 0.0f) { stepAction = true; } } updateOrientation(deltaTime); updatePosition(deltaTime); } // update sensorToWorldMatrix for camera and hand controllers // before we perform rig animations and IK. updateSensorToWorldMatrix(); { PerformanceTimer perfTimer("skeleton"); _skeletonModel->simulate(deltaTime); } // we've achived our final adjusted position and rotation for the avatar // and all of its joints, now update our attachements. Avatar::simulateAttachments(deltaTime); if (!_skeletonModel->hasSkeleton()) { // All the simulation that can be done has been done return; } { PerformanceTimer perfTimer("joints"); // copy out the skeleton joints from the model _rig->copyJointsIntoJointData(_jointData); } { PerformanceTimer perfTimer("head"); Head* head = getHead(); glm::vec3 headPosition; if (!_skeletonModel->getHeadPosition(headPosition)) { headPosition = getPosition(); } head->setPosition(headPosition); head->setScale(getUniformScale()); head->simulate(deltaTime, true); } // Record avatars movements. auto recorder = DependencyManager::get<recording::Recorder>(); if (recorder->isRecording()) { static const recording::FrameType FRAME_TYPE = recording::Frame::registerFrameType(AvatarData::FRAME_NAME); recorder->recordFrame(FRAME_TYPE, toFrame(*this)); } locationChanged(); // if a entity-child of this avatar has moved outside of its queryAACube, update the cube and tell the entity server. EntityTreeRenderer* entityTreeRenderer = qApp->getEntities(); EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr; if (entityTree) { bool flyingAllowed = true; bool ghostingAllowed = true; entityTree->withWriteLock([&] { std::shared_ptr<ZoneEntityItem> zone = entityTreeRenderer->myAvatarZone(); if (zone) { flyingAllowed = zone->getFlyingAllowed(); ghostingAllowed = zone->getGhostingAllowed(); } auto now = usecTimestampNow(); EntityEditPacketSender* packetSender = qApp->getEntityEditPacketSender(); MovingEntitiesOperator moveOperator(entityTree); forEachDescendant([&](SpatiallyNestablePointer object) { // if the queryBox has changed, tell the entity-server if (object->computePuffedQueryAACube() && object->getNestableType() == NestableType::Entity) { EntityItemPointer entity = std::static_pointer_cast<EntityItem>(object); bool success; AACube newCube = entity->getQueryAACube(success); if (success) { moveOperator.addEntityToMoveList(entity, newCube); } if (packetSender) { EntityItemProperties properties = entity->getProperties(); properties.setQueryAACubeDirty(); properties.setLastEdited(now); packetSender->queueEditEntityMessage(PacketType::EntityEdit, entityTree, entity->getID(), properties); entity->setLastBroadcast(usecTimestampNow()); } } }); // also update the position of children in our local octree if (moveOperator.hasMovingEntities()) { PerformanceTimer perfTimer("recurseTreeWithOperator"); entityTree->recurseTreeWithOperator(&moveOperator); } }); _characterController.setFlyingAllowed(flyingAllowed); if (!_characterController.isEnabled() && !ghostingAllowed) { _characterController.setEnabled(true); } } updateAvatarEntities(); } // thread-safe glm::mat4 MyAvatar::getSensorToWorldMatrix() const { return _sensorToWorldMatrixCache.get(); } // Pass a recent sample of the HMD to the avatar. // This can also update the avatar's position to follow the HMD // as it moves through the world. void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { // update the sensorMatrices based on the new hmd pose _hmdSensorMatrix = hmdSensorMatrix; _hmdSensorPosition = extractTranslation(hmdSensorMatrix); _hmdSensorOrientation = glm::quat_cast(hmdSensorMatrix); _hmdSensorFacing = getFacingDir2D(_hmdSensorOrientation); } // best called at end of main loop, after physics. // update sensor to world matrix from current body position and hmd sensor. // This is so the correct camera can be used for rendering. void MyAvatar::updateSensorToWorldMatrix() { // update the sensor mat so that the body position will end up in the desired // position when driven from the head.