Merge pull request #9049 from birarda/max-min-av-scale

add a min/max avatar scale in domain-server settings
This commit is contained in:
Seth Alves 2016-11-11 06:11:02 -08:00 committed by GitHub
commit 5629db9109
11 changed files with 190 additions and 47 deletions

View file

@ -512,12 +512,19 @@ void AvatarMixer::domainSettingsRequestComplete() {
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
nodeList->addNodeTypeToInterestSet(NodeType::Agent); nodeList->addNodeTypeToInterestSet(NodeType::Agent);
nodeList->linkedDataCreateCallback = [] (Node* node) {
node->setLinkedData(std::unique_ptr<AvatarMixerClientData> { new AvatarMixerClientData });
};
// parse the settings to pull out the values we need // parse the settings to pull out the values we need
parseDomainServerSettings(nodeList->getDomainHandler().getSettingsObject()); parseDomainServerSettings(nodeList->getDomainHandler().getSettingsObject());
float domainMinimumScale = _domainMinimumScale;
float domainMaximumScale = _domainMaximumScale;
nodeList->linkedDataCreateCallback = [domainMinimumScale, domainMaximumScale] (Node* node) {
auto clientData = std::unique_ptr<AvatarMixerClientData> { new AvatarMixerClientData };
clientData->getAvatar().setDomainMinimumScale(domainMinimumScale);
clientData->getAvatar().setDomainMaximumScale(domainMaximumScale);
node->setLinkedData(std::move(clientData));
};
// start the broadcastThread // start the broadcastThread
_broadcastThread.start(); _broadcastThread.start();
@ -549,4 +556,22 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) {
_maxKbpsPerNode = nodeBandwidthValue.toDouble(DEFAULT_NODE_SEND_BANDWIDTH) * KILO_PER_MEGA; _maxKbpsPerNode = nodeBandwidthValue.toDouble(DEFAULT_NODE_SEND_BANDWIDTH) * KILO_PER_MEGA;
qDebug() << "The maximum send bandwidth per node is" << _maxKbpsPerNode << "kbps."; qDebug() << "The maximum send bandwidth per node is" << _maxKbpsPerNode << "kbps.";
const QString AVATARS_SETTINGS_KEY = "avatars";
static const QString MIN_SCALE_OPTION = "min_avatar_scale";
float settingMinScale = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MIN_SCALE_OPTION].toDouble(MIN_AVATAR_SCALE);
_domainMinimumScale = glm::clamp(settingMinScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE);
static const QString MAX_SCALE_OPTION = "max_avatar_scale";
float settingMaxScale = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MAX_SCALE_OPTION].toDouble(MAX_AVATAR_SCALE);
_domainMaximumScale = glm::clamp(settingMaxScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE);
// make sure that the domain owner didn't flip min and max
if (_domainMinimumScale > _domainMaximumScale) {
std::swap(_domainMinimumScale, _domainMaximumScale);
}
qDebug() << "This domain requires a minimum avatar scale of" << _domainMinimumScale
<< "and a maximum avatar scale of" << _domainMaximumScale;
} }

View file

@ -59,6 +59,9 @@ private:
float _maxKbpsPerNode = 0.0f; float _maxKbpsPerNode = 0.0f;
float _domainMinimumScale { MIN_AVATAR_SCALE };
float _domainMaximumScale { MAX_AVATAR_SCALE };
QTimer* _broadcastTimer = nullptr; QTimer* _broadcastTimer = nullptr;
}; };

View file

@ -17,7 +17,7 @@
#include <AvatarData.h> #include <AvatarData.h>
#include <ScriptEngine.h> #include <ScriptEngine.h>
class ScriptableAvatar : public AvatarData, public Dependency{ class ScriptableAvatar : public AvatarData, public Dependency {
Q_OBJECT Q_OBJECT
public: public:
@ -39,4 +39,4 @@ private:
std::shared_ptr<AnimSkeleton> _animSkeleton; std::shared_ptr<AnimSkeleton> _animSkeleton;
}; };
#endif // hifi_ScriptableAvatar_h #endif // hifi_ScriptableAvatar_h

View file

@ -866,6 +866,29 @@
} }
] ]
}, },
{
"name": "avatars",
"label": "Avatars",
"assignment-types": [1, 2],
"settings": [
{
"name": "min_avatar_scale",
"type": "double",
"label": "Minimum Avatar Scale",
"help": "Limits the scale of avatars in your domain. Must be at least 0.005.",
"placeholder": 0.25,
"default": 0.25
},
{
"name": "max_avatar_scale",
"type": "double",
"label": "Maximum Avatar Scale",
"help": "Limits the scale of avatars in your domain. Cannot be greater than 1000.",
"placeholder": 3.0,
"default": 3.0
}
]
},
{ {
"name": "audio_env", "name": "audio_env",
"label": "Audio Environment", "label": "Audio Environment",

View file

@ -165,16 +165,17 @@ AABox Avatar::getBounds() const {
void Avatar::animateScaleChanges(float deltaTime) { void Avatar::animateScaleChanges(float deltaTime) {
float currentScale = getUniformScale(); float currentScale = getUniformScale();
if (currentScale != _targetScale) { auto desiredScale = getDomainLimitedScale();
// use exponential decay toward _targetScale if (currentScale != desiredScale) {
// use exponential decay toward the domain limit clamped scale
const float SCALE_ANIMATION_TIMESCALE = 0.5f; const float SCALE_ANIMATION_TIMESCALE = 0.5f;
float blendFactor = glm::clamp(deltaTime / SCALE_ANIMATION_TIMESCALE, 0.0f, 1.0f); float blendFactor = glm::clamp(deltaTime / SCALE_ANIMATION_TIMESCALE, 0.0f, 1.0f);
float animatedScale = (1.0f - blendFactor) * currentScale + blendFactor * _targetScale; float animatedScale = (1.0f - blendFactor) * currentScale + blendFactor * desiredScale;
// snap to the end when we get close enough // snap to the end when we get close enough
const float MIN_RELATIVE_SCALE_ERROR = 0.03f; const float MIN_RELATIVE_SCALE_ERROR = 0.03f;
if (fabsf(_targetScale - currentScale) / _targetScale < MIN_RELATIVE_SCALE_ERROR) { if (fabsf(desiredScale - currentScale) / desiredScale < MIN_RELATIVE_SCALE_ERROR) {
animatedScale = _targetScale; animatedScale = desiredScale;
} }
setScale(glm::vec3(animatedScale)); // avatar scale is uniform setScale(glm::vec3(animatedScale)); // avatar scale is uniform

View file

@ -152,7 +152,7 @@ bool AvatarActionHold::getTarget(float deltaTimeStep, glm::quat& rotation, glm::
Transform avatarTransform; Transform avatarTransform;
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
avatarTransform = myAvatar->getTransform(); avatarTransform = myAvatar->getTransform();
palmPosition = avatarTransform.transform(pose.getTranslation() / myAvatar->getTargetScale()); palmPosition = avatarTransform.transform(pose.getTranslation() / myAvatar->getDomainLimitedScale());
palmRotation = avatarTransform.getRotation() * pose.getRotation(); palmRotation = avatarTransform.getRotation() * pose.getRotation();
} else { } else {
glm::vec3 avatarRigidBodyPosition; glm::vec3 avatarRigidBodyPosition;

View file

@ -130,6 +130,15 @@ MyAvatar::MyAvatar(RigPointer rig) :
connect(DependencyManager::get<AddressManager>().data(), &AddressManager::locationChangeRequired, connect(DependencyManager::get<AddressManager>().data(), &AddressManager::locationChangeRequired,
this, static_cast<SlotType>(&MyAvatar::goToLocation)); this, static_cast<SlotType>(&MyAvatar::goToLocation));
// 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::clearScaleRestriction);
_characterController.setEnabled(true); _characterController.setEnabled(true);
_bodySensorMatrix = deriveBodyFromHMDSensor(); _bodySensorMatrix = deriveBodyFromHMDSensor();
@ -1823,25 +1832,104 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float
return false; return false;
} }
void MyAvatar::increaseSize() { // There can be a separation between the _targetScale and the actual scale of the rendered avatar in a domain.
if ((1.0f + SCALING_RATIO) * _targetScale < MAX_AVATAR_SCALE) { // When the avatar enters a domain where their target scale is not allowed according to the min/max
_targetScale *= (1.0f + SCALING_RATIO); // we do not change their saved target scale. Instead, we use getDomainLimitedScale() to render the avatar
qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale); // at a domain appropriate size. When the avatar leaves the limiting domain, we'll return them to their previous target scale.
// While connected to a domain that limits avatar scale if the user manually changes their avatar scale, we change
// target scale to match the new scale they have chosen. When they leave the domain they will not return to the scale they were
// before they entered the limiting domain.
void MyAvatar::clampTargetScaleToDomainLimits() {
// when we're about to change the target scale because the user has asked to increase or decrease their scale,
// we first make sure that we're starting from a target scale that is allowed by the current domain
auto clampedTargetScale = glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale);
if (clampedTargetScale != _targetScale) {
qCDebug(interfaceapp, "Clamped scale to %f since original target scale %f was not allowed by domain",
clampedTargetScale, _targetScale);
setTargetScale(clampedTargetScale);
} }
} }
void MyAvatar::clampScaleChangeToDomainLimits(float desiredScale) {
auto clampedTargetScale = glm::clamp(desiredScale, _domainMinimumScale, _domainMaximumScale);
if (clampedTargetScale != desiredScale) {
qCDebug(interfaceapp, "Forcing scale to %f since %f is not allowed by domain",
clampedTargetScale, desiredScale);
}
setTargetScale(clampedTargetScale);
qCDebug(interfaceapp, "Changed scale to %f", _targetScale);
}
void MyAvatar::increaseSize() {
// make sure we're starting from an allowable scale
clampTargetScaleToDomainLimits();
// calculate what our new scale should be
float updatedTargetScale = _targetScale * (1.0f + SCALING_RATIO);
// attempt to change to desired scale (clamped to the domain limits)
clampScaleChangeToDomainLimits(updatedTargetScale);
}
void MyAvatar::decreaseSize() { void MyAvatar::decreaseSize() {
if (MIN_AVATAR_SCALE < (1.0f - SCALING_RATIO) * _targetScale) { // make sure we're starting from an allowable scale
_targetScale *= (1.0f - SCALING_RATIO); clampTargetScaleToDomainLimits();
qCDebug(interfaceapp, "Changed scale to %f", (double)_targetScale);
} // calculate what our new scale should be
float updatedTargetScale = _targetScale * (1.0f - SCALING_RATIO);
// attempt to change to desired scale (clamped to the domain limits)
clampScaleChangeToDomainLimits(updatedTargetScale);
} }
void MyAvatar::resetSize() { void MyAvatar::resetSize() {
_targetScale = 1.0f; // attempt to reset avatar size to the default (clamped to domain limits)
qCDebug(interfaceapp, "Reset scale to %f", (double)_targetScale); const float DEFAULT_AVATAR_SCALE = 1.0f;
clampScaleChangeToDomainLimits(DEFAULT_AVATAR_SCALE);
} }
void MyAvatar::restrictScaleFromDomainSettings(const QJsonObject& domainSettingsObject) {
// pull out the minimum and maximum scale and set them to restrict our scale
static const QString AVATAR_SETTINGS_KEY = "avatars";
auto avatarsObject = domainSettingsObject[AVATAR_SETTINGS_KEY].toObject();
static const QString MIN_SCALE_OPTION = "min_avatar_scale";
float settingMinScale = avatarsObject[MIN_SCALE_OPTION].toDouble(MIN_AVATAR_SCALE);
setDomainMinimumScale(settingMinScale);
static const QString MAX_SCALE_OPTION = "max_avatar_scale";
float settingMaxScale = avatarsObject[MAX_SCALE_OPTION].toDouble(MAX_AVATAR_SCALE);
setDomainMaximumScale(settingMaxScale);
// make sure that the domain owner didn't flip min and max
if (_domainMinimumScale > _domainMaximumScale) {
std::swap(_domainMinimumScale, _domainMaximumScale);
}
qCDebug(interfaceapp, "This domain requires a minimum avatar scale of %f and a maximum avatar scale of %f",
_domainMinimumScale, _domainMaximumScale);
// debug to log if this avatar's scale in this domain will be clamped
auto clampedScale = glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale);
if (_targetScale != clampedScale) {
qCDebug(interfaceapp, "Avatar scale will be clamped to %f because %f is not allowed by current domain",
clampedScale, _targetScale);
}
}
void MyAvatar::clearScaleRestriction() {
_domainMinimumScale = MIN_AVATAR_SCALE;
_domainMaximumScale = MAX_AVATAR_SCALE;
}
void MyAvatar::goToLocation(const QVariant& propertiesVar) { void MyAvatar::goToLocation(const QVariant& propertiesVar) {
qCDebug(interfaceapp, "MyAvatar QML goToLocation"); qCDebug(interfaceapp, "MyAvatar QML goToLocation");

View file

@ -292,6 +292,9 @@ public slots:
bool shouldFaceLocation = false); bool shouldFaceLocation = false);
void goToLocation(const QVariant& properties); void goToLocation(const QVariant& properties);
void restrictScaleFromDomainSettings(const QJsonObject& domainSettingsObject);
void clearScaleRestriction();
// Set/Get update the thrust that will move the avatar around // Set/Get update the thrust that will move the avatar around
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; }; void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
glm::vec3 getThrust() { return _thrust; }; glm::vec3 getThrust() { return _thrust; };
@ -369,6 +372,8 @@ private:
virtual void updatePalms() override {} virtual void updatePalms() override {}
void lateUpdatePalms(); void lateUpdatePalms();
void clampTargetScaleToDomainLimits();
void clampScaleChangeToDomainLimits(float desiredScale);
float _driveKeys[MAX_DRIVE_KEYS]; float _driveKeys[MAX_DRIVE_KEYS];
bool _wasPushing; bool _wasPushing;

View file

@ -210,7 +210,7 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
packFloatAngleToTwoByte((uint8_t*)(header->localOrientation + 0), bodyEulerAngles.y); packFloatAngleToTwoByte((uint8_t*)(header->localOrientation + 0), bodyEulerAngles.y);
packFloatAngleToTwoByte((uint8_t*)(header->localOrientation + 1), bodyEulerAngles.x); packFloatAngleToTwoByte((uint8_t*)(header->localOrientation + 1), bodyEulerAngles.x);
packFloatAngleToTwoByte((uint8_t*)(header->localOrientation + 2), bodyEulerAngles.z); packFloatAngleToTwoByte((uint8_t*)(header->localOrientation + 2), bodyEulerAngles.z);
packFloatRatioToTwoByte((uint8_t*)(&header->scale), _targetScale); packFloatRatioToTwoByte((uint8_t*)(&header->scale), getDomainLimitedScale());
header->lookAtPosition[0] = _headData->_lookAtPosition.x; header->lookAtPosition[0] = _headData->_lookAtPosition.x;
header->lookAtPosition[1] = _headData->_lookAtPosition.y; header->lookAtPosition[1] = _headData->_lookAtPosition.y;
header->lookAtPosition[2] = _headData->_lookAtPosition.z; header->lookAtPosition[2] = _headData->_lookAtPosition.z;
@ -516,7 +516,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) {
} }
return buffer.size(); return buffer.size();
} }
_targetScale = std::max(MIN_AVATAR_SCALE, std::min(MAX_AVATAR_SCALE, scale)); setTargetScale(scale);
glm::vec3 lookAt = glm::vec3(header->lookAtPosition[0], header->lookAtPosition[1], header->lookAtPosition[2]); glm::vec3 lookAt = glm::vec3(header->lookAtPosition[0], header->lookAtPosition[1], header->lookAtPosition[2]);
if (isNaN(lookAt)) { if (isNaN(lookAt)) {
@ -1439,7 +1439,7 @@ QJsonObject AvatarData::toJson() const {
if (!success) { if (!success) {
qDebug() << "Warning -- AvatarData::toJson couldn't get avatar transform"; qDebug() << "Warning -- AvatarData::toJson couldn't get avatar transform";
} }
avatarTransform.setScale(getTargetScale()); avatarTransform.setScale(getDomainLimitedScale());
if (recordingBasis) { if (recordingBasis) {
root[JSON_AVATAR_BASIS] = Transform::toJson(*recordingBasis); root[JSON_AVATAR_BASIS] = Transform::toJson(*recordingBasis);
// Find the relative transform // Find the relative transform
@ -1451,7 +1451,7 @@ QJsonObject AvatarData::toJson() const {
root[JSON_AVATAR_RELATIVE] = Transform::toJson(avatarTransform); root[JSON_AVATAR_RELATIVE] = Transform::toJson(avatarTransform);
} }
auto scale = getTargetScale(); auto scale = getDomainLimitedScale();
if (scale != 1.0f) { if (scale != 1.0f) {
root[JSON_AVATAR_SCALE] = scale; root[JSON_AVATAR_SCALE] = scale;
} }

View file

@ -243,6 +243,12 @@ public:
void setTargetScale(float targetScale); void setTargetScale(float targetScale);
void setTargetScaleVerbose(float targetScale); void setTargetScaleVerbose(float targetScale);
float getDomainLimitedScale() const { return glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale); }
void setDomainMinimumScale(float domainMinimumScale)
{ _domainMinimumScale = glm::clamp(domainMinimumScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); }
void setDomainMaximumScale(float domainMaximumScale)
{ _domainMaximumScale = glm::clamp(domainMaximumScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); }
// Hand State // Hand State
Q_INVOKABLE void setHandState(char s) { _handState = s; } Q_INVOKABLE void setHandState(char s) { _handState = s; }
Q_INVOKABLE char getHandState() const { return _handState; } Q_INVOKABLE char getHandState() const { return _handState; }
@ -377,6 +383,8 @@ protected:
// Body scale // Body scale
float _targetScale; float _targetScale;
float _domainMinimumScale { MIN_AVATAR_SCALE };
float _domainMaximumScale { MAX_AVATAR_SCALE };
// Hand state (are we grabbing something or not) // Hand state (are we grabbing something or not)
char _handState; char _handState;

View file

@ -272,27 +272,17 @@ void DomainHandler::setIsConnected(bool isConnected) {
} }
void DomainHandler::requestDomainSettings() { void DomainHandler::requestDomainSettings() {
// TODO: the nodes basically lock if they don't get a response - add a timeout to this so that they at least restart qCDebug(networking) << "Requesting settings from domain server";
// if they can't get settings
Assignment::Type assignmentType = Assignment::typeForNodeType(DependencyManager::get<NodeList>()->getOwnerType());
NodeType_t owningNodeType = DependencyManager::get<NodeList>()->getOwnerType();
if (owningNodeType == NodeType::Agent) { auto packet = NLPacket::create(PacketType::DomainSettingsRequest, sizeof(assignmentType), true, false);
// for now the agent nodes don't need any domain settings packet->writePrimitive(assignmentType);
_settingsObject = QJsonObject();
emit settingsReceived(_settingsObject); auto nodeList = DependencyManager::get<LimitedNodeList>();
} else { nodeList->sendPacket(std::move(packet), _sockAddr);
qCDebug(networking) << "Requesting settings from domain server";
_settingsTimer.start();
Assignment::Type assignmentType = Assignment::typeForNodeType(DependencyManager::get<NodeList>()->getOwnerType());
auto packet = NLPacket::create(PacketType::DomainSettingsRequest, sizeof(assignmentType), true, false);
packet->writePrimitive(assignmentType);
auto nodeList = DependencyManager::get<LimitedNodeList>();
nodeList->sendPacket(std::move(packet), _sockAddr);
_settingsTimer.start();
}
} }
void DomainHandler::processSettingsPacketList(QSharedPointer<ReceivedMessage> packetList) { void DomainHandler::processSettingsPacketList(QSharedPointer<ReceivedMessage> packetList) {