1207 lines · 46.7 KB · C++
RawDownload
////  Avatar.cpp//  interface/src/avatar////  Created by Philip Rosedale on 9/11/12.//  Copyright 2013 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 <vector> #include <QDesktopWidget>#include <QWindow> #include <glm/glm.hpp>#include <glm/gtx/quaternion.hpp>#include <glm/gtx/vector_angle.hpp>#include <glm/gtc/type_ptr.hpp>#include <glm/gtx/vector_query.hpp> #include <DeferredLightingEffect.h>#include <GeometryUtil.h>#include <LODManager.h>#include <NodeList.h>#include <NumericalConstants.h>#include <OctreeUtils.h>#include <udt/PacketHeaders.h>#include <PerfStat.h>#include <SharedUtil.h>#include <TextRenderer3D.h>#include <TextureCache.h>#include <VariantMapToScriptValue.h>#include <DebugDraw.h> #include "Application.h"#include "Avatar.h"#include "AvatarManager.h"#include "AvatarMotionState.h"#include "Head.h"#include "Menu.h"#include "Physics.h"#include "Util.h"#include "world.h"#include "InterfaceLogging.h"#include "SoftAttachmentModel.h"#include <Rig.h> using namespace std; const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f);const int   NUM_BODY_CONE_SIDES = 9;const float CHAT_MESSAGE_SCALE = 0.0015f;const float CHAT_MESSAGE_HEIGHT = 0.1f;const float DISPLAYNAME_FADE_TIME = 0.5f;const float DISPLAYNAME_FADE_FACTOR = pow(0.01f, 1.0f / DISPLAYNAME_FADE_TIME);const float DISPLAYNAME_ALPHA = 1.0f;const float DISPLAYNAME_BACKGROUND_ALPHA = 0.4f;const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f); namespace render {    template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) {        return ItemKey::Builder::opaqueShape();    }    template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar) {        return static_pointer_cast<Avatar>(avatar)->getBounds();    }    template <> void payloadRender(const AvatarSharedPointer& avatar, RenderArgs* args) {        auto avatarPtr = static_pointer_cast<Avatar>(avatar);        if (avatarPtr->isInitialized() && args) {            PROFILE_RANGE_BATCH(*args->_batch, "renderAvatarPayload");            avatarPtr->render(args, qApp->getCamera()->getPosition());        }    }} Avatar::Avatar(RigPointer rig) :    AvatarData(),    _skeletonOffset(0.0f),    _bodyYawDelta(0.0f),    _positionDeltaAccumulator(0.0f),    _lastVelocity(0.0f),    _acceleration(0.0f),    _lastAngularVelocity(0.0f),    _lastOrientation(),    _worldUpDirection(DEFAULT_UP_DIRECTION),    _moving(false),    _initialized(false),    _voiceSphereID(GeometryCache::UNKNOWN_ID){    // we may have been created in the network thread, but we live in the main thread    moveToThread(qApp->thread());     setScale(glm::vec3(1.0f)); // avatar scale is uniform     // give the pointer to our head to inherited _headData variable from AvatarData    _headData = static_cast<HeadData*>(new Head(this));     _skeletonModel = std::make_shared<SkeletonModel>(this, nullptr, rig);} Avatar::~Avatar() {    assert(isDead()); // mark dead before calling the dtor     EntityTreeRenderer* treeRenderer = qApp->getEntities();    EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;    if (entityTree) {        entityTree->withWriteLock([&] {            AvatarEntityMap avatarEntities = getAvatarEntityData();            for (auto entityID : avatarEntities.keys()) {                entityTree->deleteEntity(entityID, true, true);            }        });    }     if (_motionState) {        delete _motionState;        _motionState = nullptr;    }} void Avatar::init() {    getHead()->init();    _skeletonModel->init();    _initialized = true;} glm::vec3 Avatar::getChestPosition() const {    // for now, let's just assume that the "chest" is halfway between the root and the neck    glm::vec3 neckPosition;    return _skeletonModel->getNeckPosition(neckPosition) ? (getPosition() + neckPosition) * 0.5f : getPosition();} glm::vec3 Avatar::getNeckPosition() const {    glm::vec3 neckPosition;    return _skeletonModel->getNeckPosition(neckPosition) ? neckPosition : getPosition();}  glm::quat Avatar::getWorldAlignedOrientation () const {    return computeRotationFromBodyToWorldUp() * getOrientation();} AABox Avatar::getBounds() const {    if (!_skeletonModel->isRenderable() || _skeletonModel->needsFixupInScene()) {        // approximately 2m tall, scaled to user request.        return AABox(getPosition() - glm::vec3(getUniformScale()), getUniformScale() * 2.0f);    }    return _skeletonModel->getRenderableMeshBound();} void Avatar::animateScaleChanges(float deltaTime) {    float currentScale = getUniformScale();    if (currentScale != _targetScale) {        // use exponential decay toward _targetScale        const float SCALE_ANIMATION_TIMESCALE = 0.5f;        float blendFactor = glm::clamp(deltaTime / SCALE_ANIMATION_TIMESCALE, 0.0f, 1.0f);        float animatedScale = (1.0f - blendFactor) * currentScale + blendFactor * _targetScale;         // snap to the end when we get close enough        const float MIN_RELATIVE_SCALE_ERROR = 0.03f;        if (fabsf(_targetScale - currentScale) / _targetScale < MIN_RELATIVE_SCALE_ERROR) {            animatedScale = _targetScale;        }         setScale(glm::vec3(animatedScale)); // avatar scale is uniform        rebuildCollisionShape();    }} void Avatar::updateAvatarEntities() {    // - if queueEditEntityMessage sees clientOnly flag it does _myAvatar->updateAvatarEntity()    // - updateAvatarEntity saves the bytes and sets _avatarEntityDataLocallyEdited    // - MyAvatar::update notices _avatarEntityDataLocallyEdited and calls sendIdentityPacket    // - sendIdentityPacket sends the entity bytes to the server which relays them to other interfaces    // - AvatarHashMap::processAvatarIdentityPacket on other interfaces call avatar->setAvatarEntityData()    // - setAvatarEntityData saves the bytes and sets _avatarEntityDataChanged = true    // - (My)Avatar::simulate notices _avatarEntityDataChanged and here we are...     if (!_avatarEntityDataChanged) {        return;    }     if (getID() == QUuid()) {        return; // wait until MyAvatar gets an ID before doing this.    }     EntityTreeRenderer* treeRenderer = qApp->getEntities();    EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;    if (!entityTree) {        return;    }     bool success = true;    QScriptEngine scriptEngine;    entityTree->withWriteLock([&] {        AvatarEntityMap avatarEntities = getAvatarEntityData();        for (auto entityID : avatarEntities.keys()) {            // see EntityEditPacketSender::queueEditEntityMessage for the other end of this.  unpack properties            // and either add or update the entity.            QByteArray jsonByteArray = avatarEntities.value(entityID);            QJsonDocument jsonProperties = QJsonDocument::fromBinaryData(jsonByteArray);            if (!jsonProperties.isObject()) {                qCDebug(interfaceapp) << "got bad avatarEntity json" << QString(jsonByteArray.toHex());                continue;            }             QVariant variantProperties = jsonProperties.toVariant();            QVariantMap asMap = variantProperties.toMap();            QScriptValue scriptProperties = variantMapToScriptValue(asMap, scriptEngine);            EntityItemProperties properties;            EntityItemPropertiesFromScriptValueHonorReadOnly(scriptProperties, properties);            properties.setClientOnly(true);            properties.setOwningAvatarID(getID());             // there's no entity-server to tell us we're the simulation owner, so always set the            // simulationOwner to the owningAvatarID and a high priority.            properties.setSimulationOwner(getID(), AVATAR_ENTITY_SIMULATION_PRIORITY);             if (properties.getParentID() == AVATAR_SELF_ID) {                properties.setParentID(getID());            }             EntityItemPointer entity = entityTree->findEntityByEntityItemID(EntityItemID(entityID));             if (entity) {                if (entityTree->updateEntity(entityID, properties)) {                    entity->updateLastEditedFromRemote();                } else {                    success = false;                }            } else {                entity = entityTree->addEntity(entityID, properties);                if (!entity) {                    success = false;                }            }        }         AvatarEntityIDs recentlyDettachedAvatarEntities = getAndClearRecentlyDetachedIDs();        _avatarEntitiesLock.withReadLock([&] {            foreach (auto entityID, recentlyDettachedAvatarEntities) {                if (!_avatarEntityData.contains(entityID)) {                    entityTree->deleteEntity(entityID, true, true);                }            }        });    });     if (success) {        setAvatarEntityDataChanged(false);    }}   void Avatar::simulate(float deltaTime) {    PerformanceTimer perfTimer("simulate");     if (!isDead() && !_motionState) {        DependencyManager::get<AvatarManager>()->addAvatarToSimulation(this);    }    animateScaleChanges(deltaTime);     // update the shouldAnimate flag to match whether or not we will render the avatar.    const float MINIMUM_VISIBILITY_FOR_ON = 0.4f;    const float MAXIMUM_VISIBILITY_FOR_OFF = 0.6f;    ViewFrustum viewFrustum;    qApp->copyViewFrustum(viewFrustum);    float visibility = calculateRenderAccuracy(viewFrustum.getPosition(),            getBounds(), DependencyManager::get<LODManager>()->getOctreeSizeScale());    if (!_shouldAnimate) {        if (visibility > MINIMUM_VISIBILITY_FOR_ON) {            _shouldAnimate = true;            qCDebug(interfaceapp) << "Restoring" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for visibility" << visibility;        }    } else if (visibility < MAXIMUM_VISIBILITY_FOR_OFF) {        _shouldAnimate = false;        qCDebug(interfaceapp) << "Optimizing" << (isMyAvatar() ? "myself" : getSessionUUID()) << "for visibility" << visibility;    }     // simple frustum check    float boundingRadius = getBoundingRadius();    qApp->copyDisplayViewFrustum(viewFrustum);    bool avatarPositionInView = viewFrustum.sphereIntersectsFrustum(getPosition(), boundingRadius);    bool avatarMeshInView = viewFrustum.boxIntersectsFrustum(_skeletonModel->getRenderableMeshBound());     if (_shouldAnimate && !_shouldSkipRender && (avatarPositionInView || avatarMeshInView)) {        {            PerformanceTimer perfTimer("skeleton");            _skeletonModel->getRig()->copyJointsFromJointData(_jointData);            _skeletonModel->simulate(deltaTime, _hasNewJointRotations || _hasNewJointTranslations);            locationChanged(); // joints changed, so if there are any children, update them.            _hasNewJointRotations = false;            _hasNewJointTranslations = false;        }        {            PerformanceTimer perfTimer("head");            glm::vec3 headPosition = getPosition();            _skeletonModel->getHeadPosition(headPosition);            Head* head = getHead();            head->setPosition(headPosition);            head->setScale(getUniformScale());            head->simulate(deltaTime, false, !_shouldAnimate);        }    } else {        // a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated.        _skeletonModel->simulate(deltaTime, false);    }     // update animation for display name fade in/out    if ( _displayNameTargetAlpha != _displayNameAlpha) {        // the alpha function is        // Fade out => alpha(t) = factor ^ t => alpha(t+dt) = alpha(t) * factor^(dt)        // Fade in  => alpha(t) = 1 - factor^t => alpha(t+dt) = 1-(1-alpha(t))*coef^(dt)        // factor^(dt) = coef        float coef = pow(DISPLAYNAME_FADE_FACTOR, deltaTime);        if (_displayNameTargetAlpha < _displayNameAlpha) {            // Fading out            _displayNameAlpha *= coef;        } else {            // Fading in            _displayNameAlpha = 1 - (1 - _displayNameAlpha) * coef;        }        _displayNameAlpha = abs(_displayNameAlpha - _displayNameTargetAlpha) < 0.01f ? _displayNameTargetAlpha : _displayNameAlpha;    }     measureMotionDerivatives(deltaTime);     simulateAttachments(deltaTime);    updatePalms();    updateAvatarEntities();} bool Avatar::isLookingAtMe(AvatarSharedPointer avatar) const {    const float HEAD_SPHERE_RADIUS = 0.1f;    glm::vec3 theirLookAt = dynamic_pointer_cast<Avatar>(avatar)->getHead()->getLookAtPosition();    glm::vec3 myEyePosition = getHead()->getEyePosition();     return glm::distance(theirLookAt, myEyePosition) <= (HEAD_SPHERE_RADIUS * getUniformScale());} void Avatar::slamPosition(const glm::vec3& newPosition) {    setPosition(newPosition);    _positionDeltaAccumulator = glm::vec3(0.0f);    setVelocity(glm::vec3(0.0f));    _lastVelocity = glm::vec3(0.0f);} void Avatar::applyPositionDelta(const glm::vec3& delta) {    setPosition(getPosition() + delta);    _positionDeltaAccumulator += delta;} void Avatar::measureMotionDerivatives(float deltaTime) {    // linear    float invDeltaTime = 1.0f / deltaTime;    // Floating point error prevents us from computing velocity in a naive way    // (e.g. vel = (pos - oldPos) / dt) so instead we use _positionOffsetAccumulator.    glm::vec3 velocity = _positionDeltaAccumulator * invDeltaTime;    _positionDeltaAccumulator = glm::vec3(0.0f);    _acceleration = (velocity - _lastVelocity) * invDeltaTime;    _lastVelocity = velocity;    setVelocity(velocity);     // angular    glm::quat orientation = getOrientation();    glm::quat delta = glm::inverse(_lastOrientation) * orientation;    glm::vec3 angularVelocity = glm::axis(delta) * glm::angle(delta) * invDeltaTime;    setAngularVelocity(angularVelocity);    _lastOrientation = getOrientation();} enum TextRendererType {    CHAT,    DISPLAYNAME}; static TextRenderer3D* textRenderer(TextRendererType type) {    static TextRenderer3D* chatRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY, -1,        false, SHADOW_EFFECT);    static TextRenderer3D* displayNameRenderer = TextRenderer3D::getInstance(SANS_FONT_FAMILY);     switch(type) {    case CHAT:        return chatRenderer;    case DISPLAYNAME:        return displayNameRenderer;    }     return displayNameRenderer;} bool Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {    auto avatarPayload = new render::Payload<AvatarData>(self);    auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload);    _renderItemID = scene->allocateID();    pendingChanges.resetItem(_renderItemID, avatarPayloadPointer);    _skeletonModel->addToScene(scene, pendingChanges);     for (auto& attachmentModel : _attachmentModels) {        attachmentModel->addToScene(scene, pendingChanges);    }     return true;} void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges) {    pendingChanges.removeItem(_renderItemID);    render::Item::clearID(_renderItemID);    _skeletonModel->removeFromScene(scene, pendingChanges);    for (auto& attachmentModel : _attachmentModels) {        attachmentModel->removeFromScene(scene, pendingChanges);    }} void Avatar::updateRenderItem(render::PendingChanges& pendingChanges) {    if (render::Item::isValidID(_renderItemID)) {        pendingChanges.updateItem<render::Payload<AvatarData>>(_renderItemID, [](render::Payload<AvatarData>& p) {});    }} void Avatar::postUpdate(float deltaTime) {     bool renderLookAtVectors;    if (isMyAvatar()) {        renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::RenderMyLookAtVectors);    } else {        renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::RenderOtherLookAtVectors);    }     if (renderLookAtVectors) {        const float EYE_RAY_LENGTH = 10.0;        const glm::vec4 BLUE(0.0f, 0.0f, 1.0f, 1.0f);        const glm::vec4 RED(1.0f, 0.0f, 0.0f, 1.0f);         int leftEyeJoint = getJointIndex("LeftEye");        glm::vec3 leftEyePosition;        glm::quat leftEyeRotation;         if (_skeletonModel->getJointPositionInWorldFrame(leftEyeJoint, leftEyePosition) &&            _skeletonModel->getJointRotationInWorldFrame(leftEyeJoint, leftEyeRotation)) {            DebugDraw::getInstance().drawRay(leftEyePosition, leftEyePosition + leftEyeRotation * Vectors::UNIT_Z * EYE_RAY_LENGTH, BLUE);        }         int rightEyeJoint = getJointIndex("RightEye");        glm::vec3 rightEyePosition;        glm::quat rightEyeRotation;        if (_skeletonModel->getJointPositionInWorldFrame(rightEyeJoint, rightEyePosition) &&            _skeletonModel->getJointRotationInWorldFrame(rightEyeJoint, rightEyeRotation)) {            DebugDraw::getInstance().drawRay(rightEyePosition, rightEyePosition + rightEyeRotation * Vectors::UNIT_Z * EYE_RAY_LENGTH, RED);        }    }} void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {    auto& batch = *renderArgs->_batch;    PROFILE_RANGE_BATCH(batch, __FUNCTION__);     if (glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(), getPosition()) < 10.0f) {        auto geometryCache = DependencyManager::get<GeometryCache>();         // render pointing lasers        glm::vec3 laserColor = glm::vec3(1.0f, 0.0f, 1.0f);        float laserLength = 50.0f;        glm::vec3 position;        glm::quat rotation;        bool havePosition, haveRotation;         if (_handState & LEFT_HAND_POINTING_FLAG) {             if (_handState & IS_FINGER_POINTING_FLAG) {                int leftIndexTip = getJointIndex("LeftHandIndex4");                int leftIndexTipJoint = getJointIndex("LeftHandIndex3");                havePosition = _skeletonModel->getJointPositionInWorldFrame(leftIndexTip, position);                haveRotation = _skeletonModel->getJointRotationInWorldFrame(leftIndexTipJoint, rotation);            } else {                int leftHand = _skeletonModel->getLeftHandJointIndex();                havePosition = _skeletonModel->getJointPositionInWorldFrame(leftHand, position);                haveRotation = _skeletonModel->getJointRotationInWorldFrame(leftHand, rotation);            }             if (havePosition && haveRotation) {                PROFILE_RANGE_BATCH(batch, __FUNCTION__":leftHandPointer");                Transform pointerTransform;                pointerTransform.setTranslation(position);                pointerTransform.setRotation(rotation);                batch.setModelTransform(pointerTransform);                geometryCache->bindSimpleProgram(batch);                geometryCache->renderLine(batch, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, laserLength, 0.0f), laserColor);            }        }         if (_handState & RIGHT_HAND_POINTING_FLAG) {             if (_handState & IS_FINGER_POINTING_FLAG) {                int rightIndexTip = getJointIndex("RightHandIndex4");                int rightIndexTipJoint = getJointIndex("RightHandIndex3");                havePosition = _skeletonModel->getJointPositionInWorldFrame(rightIndexTip, position);

Lines 1–500 of 1207.