mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-04-10 10:34:56 +02:00
Merge pull request #14079 from luiscuenca/simpleAvatarTransit
Simple avatar transit
This commit is contained in:
commit
6290b56383
13 changed files with 236 additions and 10 deletions
|
@ -48,6 +48,10 @@ AvatarActionHold::~AvatarActionHold() {
|
|||
myAvatar->removeHoldAction(this);
|
||||
}
|
||||
}
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (ownerEntity) {
|
||||
ownerEntity->setTransitingWithAvatar(false);
|
||||
}
|
||||
|
||||
#if WANT_DEBUG
|
||||
qDebug() << "AvatarActionHold::~AvatarActionHold" << (void*)this;
|
||||
|
@ -131,6 +135,15 @@ bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm::
|
|||
glm::vec3 palmPosition;
|
||||
glm::quat palmRotation;
|
||||
|
||||
bool isTransitingWithAvatar = holdingAvatar->getTransit()->isTransiting();
|
||||
if (isTransitingWithAvatar != _isTransitingWithAvatar) {
|
||||
_isTransitingWithAvatar = isTransitingWithAvatar;
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (ownerEntity) {
|
||||
ownerEntity->setTransitingWithAvatar(_isTransitingWithAvatar);
|
||||
}
|
||||
}
|
||||
|
||||
if (holdingAvatar->isMyAvatar()) {
|
||||
std::shared_ptr<MyAvatar> myAvatar = avatarManager->getMyAvatar();
|
||||
|
||||
|
@ -404,11 +417,14 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) {
|
|||
_kinematicSetVelocity = kinematicSetVelocity;
|
||||
_ignoreIK = ignoreIK;
|
||||
_active = true;
|
||||
|
||||
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||
|
||||
auto ownerEntity = _ownerEntity.lock();
|
||||
if (ownerEntity) {
|
||||
ownerEntity->setDynamicDataDirty(true);
|
||||
ownerEntity->setDynamicDataNeedsTransmit(true);
|
||||
ownerEntity->setDynamicDataNeedsTransmit(true);
|
||||
ownerEntity->setTransitingWithAvatar(myAvatar->getTransit()->isTransiting());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -59,6 +59,8 @@ private:
|
|||
bool _kinematicSetVelocity { false };
|
||||
bool _previousSet { false };
|
||||
bool _ignoreIK { false };
|
||||
bool _isTransitingWithAvatar { false };
|
||||
|
||||
glm::vec3 _previousPositionalTarget;
|
||||
glm::quat _previousRotationalTarget;
|
||||
|
||||
|
|
|
@ -78,6 +78,15 @@ AvatarManager::AvatarManager(QObject* parent) :
|
|||
removeAvatar(nodeID, KillAvatarReason::AvatarIgnored);
|
||||
}
|
||||
});
|
||||
|
||||
const float AVATAR_TRANSIT_TRIGGER_DISTANCE = 1.0f;
|
||||
const int AVATAR_TRANSIT_FRAME_COUNT = 11; // Based on testing
|
||||
const int AVATAR_TRANSIT_FRAMES_PER_METER = 1; // Based on testing
|
||||
|
||||
_transitConfig._totalFrames = AVATAR_TRANSIT_FRAME_COUNT;
|
||||
_transitConfig._triggerDistance = AVATAR_TRANSIT_TRIGGER_DISTANCE;
|
||||
_transitConfig._framesPerMeter = AVATAR_TRANSIT_FRAMES_PER_METER;
|
||||
_transitConfig._isDistanceBased = true;
|
||||
}
|
||||
|
||||
AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) {
|
||||
|
@ -129,6 +138,10 @@ void AvatarManager::updateMyAvatar(float deltaTime) {
|
|||
bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||
PerformanceWarning warn(showWarnings, "AvatarManager::updateMyAvatar()");
|
||||
|
||||
AvatarTransit::Status status = _myAvatar->updateTransit(deltaTime, _myAvatar->getNextPosition(), _transitConfig);
|
||||
bool sendFirstTransitPackage = (status == AvatarTransit::Status::START_TRANSIT);
|
||||
bool blockTransitData = (status == AvatarTransit::Status::TRANSITING);
|
||||
|
||||
_myAvatar->update(deltaTime);
|
||||
render::Transaction transaction;
|
||||
_myAvatar->updateRenderItem(transaction);
|
||||
|
@ -137,9 +150,13 @@ void AvatarManager::updateMyAvatar(float deltaTime) {
|
|||
quint64 now = usecTimestampNow();
|
||||
quint64 dt = now - _lastSendAvatarDataTime;
|
||||
|
||||
if (dt > MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS && !_myAvatarDataPacketsPaused) {
|
||||
|
||||
if (sendFirstTransitPackage || (dt > MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS && !_myAvatarDataPacketsPaused && !blockTransitData)) {
|
||||
// send head/hand data to the avatar mixer and voxel server
|
||||
PerformanceTimer perfTimer("send");
|
||||
if (sendFirstTransitPackage) {
|
||||
_myAvatar->overrideNextPackagePositionData(_myAvatar->getTransit()->getEndPosition());
|
||||
}
|
||||
_myAvatar->sendAvatarDataPacket();
|
||||
_lastSendAvatarDataTime = now;
|
||||
_myAvatarSendRate.increment();
|
||||
|
@ -258,6 +275,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
|||
if (inView && avatar->hasNewJointData()) {
|
||||
numAvatarsUpdated++;
|
||||
}
|
||||
avatar->_transit.update(deltaTime, avatar->_globalPosition, _transitConfig);
|
||||
avatar->simulate(deltaTime, inView);
|
||||
avatar->updateRenderItem(renderTransaction);
|
||||
avatar->updateSpaceProxy(workloadTransaction);
|
||||
|
@ -811,7 +829,7 @@ void AvatarManager::setAvatarSortCoefficient(const QString& name, const QScriptV
|
|||
}
|
||||
}
|
||||
|
||||
QVariantMap AvatarManager::getPalData(const QList<QString> specificAvatarIdentifiers) {
|
||||
QVariantMap AvatarManager::getPalData(const QList<QString> specificAvatarIdentifiers) {
|
||||
QJsonArray palData;
|
||||
|
||||
auto avatarMap = getHashCopy();
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "MyAvatar.h"
|
||||
#include "OtherAvatar.h"
|
||||
|
||||
|
||||
using SortedAvatar = std::pair<float, std::shared_ptr<Avatar>>;
|
||||
|
||||
/**jsdoc
|
||||
|
@ -232,6 +233,8 @@ private:
|
|||
mutable std::mutex _spaceLock;
|
||||
workload::SpacePointer _space;
|
||||
std::vector<int32_t> _spaceProxiesToDelete;
|
||||
|
||||
AvatarTransit::TransitConfig _transitConfig;
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarManager_h
|
||||
|
|
|
@ -638,9 +638,8 @@ void MyAvatar::updateChildCauterization(SpatiallyNestablePointer object, bool ca
|
|||
|
||||
void MyAvatar::simulate(float deltaTime) {
|
||||
PerformanceTimer perfTimer("simulate");
|
||||
|
||||
animateScaleChanges(deltaTime);
|
||||
|
||||
|
||||
setFlyingEnabled(getFlyingEnabled());
|
||||
|
||||
if (_cauterizationNeedsUpdate) {
|
||||
|
@ -928,6 +927,7 @@ void MyAvatar::updateSensorToWorldMatrix() {
|
|||
updateJointFromController(controller::Action::RIGHT_HAND, _controllerRightHandMatrixCache);
|
||||
|
||||
if (hasSensorToWorldScaleChanged) {
|
||||
setTransitScale(sensorToWorldScale);
|
||||
emit sensorToWorldScaleChanged(sensorToWorldScale);
|
||||
}
|
||||
|
||||
|
|
|
@ -1115,6 +1115,8 @@ public:
|
|||
virtual QVariantList getAttachmentsVariant() const override;
|
||||
virtual void setAttachmentsVariant(const QVariantList& variant) override;
|
||||
|
||||
glm::vec3 getNextPosition() { return _goToPending ? _goToPosition : getWorldPosition(); };
|
||||
|
||||
public slots:
|
||||
|
||||
/**jsdoc
|
||||
|
|
|
@ -113,6 +113,80 @@ void Avatar::setShowNamesAboveHeads(bool show) {
|
|||
showNamesAboveHeads = show;
|
||||
}
|
||||
|
||||
AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config) {
|
||||
glm::vec3 currentPosition = _isTransiting ? _currentPosition : avatarPosition;
|
||||
float oneFrameDistance = glm::length(currentPosition - _lastPosition);
|
||||
const float MAX_TRANSIT_DISTANCE = 30.0f;
|
||||
float scaledMaxTransitDistance = MAX_TRANSIT_DISTANCE * _scale;
|
||||
if (oneFrameDistance > config._triggerDistance && oneFrameDistance < scaledMaxTransitDistance && !_isTransiting) {
|
||||
start(deltaTime, _lastPosition, currentPosition, config);
|
||||
}
|
||||
_lastPosition = currentPosition;
|
||||
_status = updatePosition(deltaTime);
|
||||
return _status;
|
||||
}
|
||||
|
||||
void AvatarTransit::start(float deltaTime, const glm::vec3& startPosition, const glm::vec3& endPosition, const AvatarTransit::TransitConfig& config) {
|
||||
_startPosition = startPosition;
|
||||
_endPosition = endPosition;
|
||||
|
||||
_transitLine = endPosition - startPosition;
|
||||
_totalDistance = glm::length(_transitLine);
|
||||
_easeType = config._easeType;
|
||||
const float REFERENCE_FRAMES_PER_SECOND = 30.0f;
|
||||
|
||||
int transitFrames = (!config._isDistanceBased) ? config._totalFrames : config._framesPerMeter * _totalDistance;
|
||||
_totalTime = (float)transitFrames / REFERENCE_FRAMES_PER_SECOND;
|
||||
_currentTime = 0.0f;
|
||||
_isTransiting = true;
|
||||
}
|
||||
|
||||
float AvatarTransit::getEaseValue(AvatarTransit::EaseType type, float value) {
|
||||
switch (type) {
|
||||
case EaseType::NONE:
|
||||
return value;
|
||||
break;
|
||||
case EaseType::EASE_IN:
|
||||
return value * value;
|
||||
break;
|
||||
case EaseType::EASE_OUT:
|
||||
return value * (2.0f - value);
|
||||
break;
|
||||
case EaseType::EASE_IN_OUT:
|
||||
return (value < 0.5f) ? 2.0f * value * value : -1.0f + (4.0f - 2.0f * value) * value;
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
AvatarTransit::Status AvatarTransit::updatePosition(float deltaTime) {
|
||||
Status status = Status::IDLE;
|
||||
if (_isTransiting) {
|
||||
float nextTime = _currentTime + deltaTime;
|
||||
glm::vec3 newPosition;
|
||||
if (nextTime >= _totalTime) {
|
||||
_currentPosition = _endPosition;
|
||||
_isTransiting = false;
|
||||
status = Status::END_TRANSIT;
|
||||
} else {
|
||||
if (_currentTime == 0) {
|
||||
status = Status::START_TRANSIT;
|
||||
} else {
|
||||
status = Status::TRANSITING;
|
||||
}
|
||||
float percentageIntoTransit = nextTime / _totalTime;
|
||||
_currentPosition = _startPosition + getEaseValue(_easeType, percentageIntoTransit) * _transitLine;
|
||||
}
|
||||
_currentTime = nextTime;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
bool AvatarTransit::getNextPosition(glm::vec3& nextPosition) {
|
||||
nextPosition = _currentPosition;
|
||||
return _isTransiting;
|
||||
}
|
||||
|
||||
Avatar::Avatar(QThread* thread) :
|
||||
_voiceSphereID(GeometryCache::UNKNOWN_ID)
|
||||
{
|
||||
|
@ -449,7 +523,18 @@ void Avatar::relayJointDataToChildren() {
|
|||
|
||||
void Avatar::simulate(float deltaTime, bool inView) {
|
||||
PROFILE_RANGE(simulation, "simulate");
|
||||
|
||||
|
||||
if (_transit.isTransiting()) {
|
||||
glm::vec3 nextPosition;
|
||||
if (_transit.getNextPosition(nextPosition)) {
|
||||
_globalPosition = nextPosition;
|
||||
_globalPositionChanged = usecTimestampNow();
|
||||
if (!hasParent()) {
|
||||
setLocalPosition(nextPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_simulationRate.increment();
|
||||
if (inView) {
|
||||
_simulationInViewRate.increment();
|
||||
|
@ -460,7 +545,7 @@ void Avatar::simulate(float deltaTime, bool inView) {
|
|||
PROFILE_RANGE(simulation, "updateJoints");
|
||||
if (inView) {
|
||||
Head* head = getHead();
|
||||
if (_hasNewJointData) {
|
||||
if (_hasNewJointData || _transit.isTransiting()) {
|
||||
_skeletonModel->getRig().copyJointsFromJointData(_jointData);
|
||||
glm::mat4 rootTransform = glm::scale(_skeletonModel->getScale()) * glm::translate(_skeletonModel->getOffset());
|
||||
_skeletonModel->getRig().computeExternalPoses(rootTransform);
|
||||
|
@ -1881,6 +1966,22 @@ float Avatar::getUnscaledEyeHeightFromSkeleton() const {
|
|||
}
|
||||
}
|
||||
|
||||
AvatarTransit::Status Avatar::updateTransit(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config) {
|
||||
std::lock_guard<std::mutex> lock(_transitLock);
|
||||
return _transit.update(deltaTime, avatarPosition, config);
|
||||
}
|
||||
|
||||
void Avatar::setTransitScale(float scale) {
|
||||
std::lock_guard<std::mutex> lock(_transitLock);
|
||||
return _transit.setScale(scale);
|
||||
}
|
||||
|
||||
void Avatar::overrideNextPackagePositionData(const glm::vec3& position) {
|
||||
std::lock_guard<std::mutex> lock(_transitLock);
|
||||
_overrideGlobalPosition = true;
|
||||
_globalPositionOverride = position;
|
||||
}
|
||||
|
||||
void Avatar::addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) {
|
||||
std::lock_guard<std::mutex> lock(_materialsLock);
|
||||
_materials[parentMaterialName].push(material);
|
||||
|
|
|
@ -50,6 +50,62 @@ enum ScreenTintLayer {
|
|||
|
||||
class Texture;
|
||||
|
||||
class AvatarTransit {
|
||||
public:
|
||||
enum Status {
|
||||
IDLE = 0,
|
||||
START_TRANSIT,
|
||||
TRANSITING,
|
||||
END_TRANSIT
|
||||
};
|
||||
|
||||
enum EaseType {
|
||||
NONE = 0,
|
||||
EASE_IN,
|
||||
EASE_OUT,
|
||||
EASE_IN_OUT
|
||||
};
|
||||
|
||||
struct TransitConfig {
|
||||
TransitConfig() {};
|
||||
int _totalFrames { 0 };
|
||||
int _framesPerMeter { 0 };
|
||||
bool _isDistanceBased { false };
|
||||
float _triggerDistance { 0 };
|
||||
EaseType _easeType { EaseType::EASE_OUT };
|
||||
};
|
||||
|
||||
AvatarTransit() {};
|
||||
Status update(float deltaTime, const glm::vec3& avatarPosition, const TransitConfig& config);
|
||||
Status getStatus() { return _status; }
|
||||
bool isTransiting() { return _isTransiting; }
|
||||
glm::vec3 getCurrentPosition() { return _currentPosition; }
|
||||
bool getNextPosition(glm::vec3& nextPosition);
|
||||
glm::vec3 getEndPosition() { return _endPosition; }
|
||||
float getTransitTime() { return _totalTime; }
|
||||
void setScale(float scale) { _scale = scale; }
|
||||
|
||||
private:
|
||||
Status updatePosition(float deltaTime);
|
||||
void start(float deltaTime, const glm::vec3& startPosition, const glm::vec3& endPosition, const TransitConfig& config);
|
||||
float getEaseValue(AvatarTransit::EaseType type, float value);
|
||||
bool _isTransiting { false };
|
||||
|
||||
glm::vec3 _startPosition;
|
||||
glm::vec3 _endPosition;
|
||||
glm::vec3 _currentPosition;
|
||||
|
||||
glm::vec3 _lastPosition;
|
||||
|
||||
glm::vec3 _transitLine;
|
||||
float _totalDistance { 0.0f };
|
||||
float _totalTime { 0.0f };
|
||||
float _currentTime { 0.0f };
|
||||
EaseType _easeType { EaseType::EASE_OUT };
|
||||
Status _status { Status::IDLE };
|
||||
float _scale { 1.0f };
|
||||
};
|
||||
|
||||
class Avatar : public AvatarData, public scriptable::ModelProvider {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -370,6 +426,13 @@ public:
|
|||
|
||||
virtual scriptable::ScriptableModelBase getScriptableModel() override;
|
||||
|
||||
std::shared_ptr<AvatarTransit> getTransit() { return std::make_shared<AvatarTransit>(_transit); };
|
||||
|
||||
AvatarTransit::Status updateTransit(float deltaTime, const glm::vec3& avatarPosition, const AvatarTransit::TransitConfig& config);
|
||||
void setTransitScale(float scale);
|
||||
|
||||
void overrideNextPackagePositionData(const glm::vec3& position);
|
||||
|
||||
signals:
|
||||
void targetScaleChanged(float targetScale);
|
||||
|
||||
|
@ -505,6 +568,7 @@ protected:
|
|||
RateCounter<> _skeletonModelSimulationRate;
|
||||
RateCounter<> _jointDataSimulationRate;
|
||||
|
||||
|
||||
protected:
|
||||
class AvatarEntityDataHash {
|
||||
public:
|
||||
|
@ -528,6 +592,9 @@ protected:
|
|||
bool _reconstructSoftEntitiesJointMap { false };
|
||||
float _modelScale { 1.0f };
|
||||
|
||||
AvatarTransit _transit;
|
||||
std::mutex _transitLock;
|
||||
|
||||
static int _jointConesID;
|
||||
|
||||
int _voiceSphereID;
|
||||
|
|
|
@ -369,7 +369,12 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
|
||||
if (hasAvatarGlobalPosition) {
|
||||
auto startSection = destinationBuffer;
|
||||
AVATAR_MEMCPY(_globalPosition);
|
||||
if (_overrideGlobalPosition) {
|
||||
AVATAR_MEMCPY(_globalPositionOverride);
|
||||
} else {
|
||||
AVATAR_MEMCPY(_globalPosition);
|
||||
}
|
||||
|
||||
|
||||
int numBytes = destinationBuffer - startSection;
|
||||
|
||||
|
@ -2088,6 +2093,10 @@ void AvatarData::sendAvatarDataPacket(bool sendAll) {
|
|||
}
|
||||
}
|
||||
|
||||
if (_overrideGlobalPosition) {
|
||||
_overrideGlobalPosition = false;
|
||||
}
|
||||
|
||||
doneEncoding(cullSmallData);
|
||||
|
||||
static AvatarDataSequenceNumber sequenceNumber = 0;
|
||||
|
|
|
@ -1372,7 +1372,8 @@ protected:
|
|||
// where Entities are located. This is currently only used by the mixer to decide how often to send
|
||||
// updates about one avatar to another.
|
||||
glm::vec3 _globalPosition { 0, 0, 0 };
|
||||
|
||||
glm::vec3 _globalPositionOverride { 0, 0, 0 };
|
||||
bool _overrideGlobalPosition { false };
|
||||
|
||||
quint64 _globalPositionChanged { 0 };
|
||||
quint64 _avatarBoundingBoxChanged { 0 };
|
||||
|
|
|
@ -266,6 +266,7 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer<ReceivedMessag
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// have the matching (or new) avatar parse the data from the packet
|
||||
int bytesRead = avatar->parseDataFromBuffer(byteArray);
|
||||
message->seek(positionBeforeRead + bytesRead);
|
||||
|
@ -316,7 +317,6 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer<ReceivedMessage>
|
|||
// In this case, the "sendingNode" is the Avatar Mixer.
|
||||
avatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged);
|
||||
_replicas.processAvatarIdentity(identityUUID, message->getMessage(), identityChanged, displayNameChanged);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -329,6 +329,7 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer<ReceivedMessage> mess
|
|||
// grab the avatar so we can ask it to process trait data
|
||||
bool isNewAvatar;
|
||||
auto avatar = newOrExistingAvatar(avatarID, sendingNode, isNewAvatar);
|
||||
|
||||
// read the first trait type for this avatar
|
||||
AvatarTraits::TraitType traitType;
|
||||
message->readPrimitive(&traitType);
|
||||
|
|
|
@ -441,6 +441,8 @@ public:
|
|||
|
||||
void setDynamicDataNeedsTransmit(bool value) const { _dynamicDataNeedsTransmit = value; }
|
||||
bool dynamicDataNeedsTransmit() const { return _dynamicDataNeedsTransmit; }
|
||||
void setTransitingWithAvatar(bool value) { _transitingWithAvatar = value; }
|
||||
bool getTransitingWithAvatar() { return _transitingWithAvatar; }
|
||||
|
||||
bool shouldSuppressLocationEdits() const;
|
||||
|
||||
|
@ -668,6 +670,7 @@ protected:
|
|||
QUuid _sourceUUID; /// the server node UUID we came from
|
||||
|
||||
bool _clientOnly { false };
|
||||
bool _transitingWithAvatar{ false };
|
||||
QUuid _owningAvatarID;
|
||||
|
||||
// physics related changes from the network to suppress any duplicates and make
|
||||
|
|
|
@ -432,6 +432,9 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
|
|||
// this case is prevented by setting _ownershipState to UNOWNABLE in EntityMotionState::ctor
|
||||
assert(!(_entity->getClientOnly() && _entity->getOwningAvatarID() != Physics::getSessionUUID()));
|
||||
|
||||
if (_entity->getTransitingWithAvatar()) {
|
||||
return false;
|
||||
}
|
||||
if (_entity->dynamicDataNeedsTransmit()) {
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue