mirror of
https://github.com/AleziaKurdis/overte.git
synced 2025-04-08 06:32:35 +02:00
Change domain setting from min/max avatar scale to min/max avatar height
* Domain settings version has been bumped from version 2.0 to 2.1 * Old domain settings for avatar scale will be auto-converted to avatar height * Avatar code has been changed so that limitDomainScale() works with the new height limits * Avatar getUnscaledEyeHeight() was added to C++. * MyAvatar.getHeight() was added to JS.
This commit is contained in:
parent
0945c1c50b
commit
9f54ce55f3
15 changed files with 226 additions and 138 deletions
|
@ -870,8 +870,8 @@ AvatarMixerClientData* AvatarMixer::getOrCreateClientData(SharedNodePointer node
|
|||
node->setLinkedData(std::unique_ptr<NodeData> { new AvatarMixerClientData(node->getUUID()) });
|
||||
clientData = dynamic_cast<AvatarMixerClientData*>(node->getLinkedData());
|
||||
auto& avatar = clientData->getAvatar();
|
||||
avatar.setDomainMinimumScale(_domainMinimumScale);
|
||||
avatar.setDomainMaximumScale(_domainMaximumScale);
|
||||
avatar.setDomainMinimumHeight(_domainMinimumHeight);
|
||||
avatar.setDomainMaximumHeight(_domainMaximumHeight);
|
||||
}
|
||||
|
||||
return clientData;
|
||||
|
@ -939,21 +939,21 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) {
|
|||
|
||||
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 MIN_HEIGHT_OPTION = "min_avatar_height";
|
||||
float settingMinHeight = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MIN_HEIGHT_OPTION].toDouble(MIN_AVATAR_HEIGHT);
|
||||
_domainMinimumHeight = glm::clamp(settingMinHeight, MIN_AVATAR_HEIGHT, MAX_AVATAR_HEIGHT);
|
||||
|
||||
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);
|
||||
static const QString MAX_HEIGHT_OPTION = "max_avatar_height";
|
||||
float settingMaxHeight = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MAX_HEIGHT_OPTION].toDouble(MAX_AVATAR_HEIGHT);
|
||||
_domainMaximumHeight = glm::clamp(settingMaxHeight, MIN_AVATAR_HEIGHT, MAX_AVATAR_HEIGHT);
|
||||
|
||||
// make sure that the domain owner didn't flip min and max
|
||||
if (_domainMinimumScale > _domainMaximumScale) {
|
||||
std::swap(_domainMinimumScale, _domainMaximumScale);
|
||||
if (_domainMinimumHeight > _domainMaximumHeight) {
|
||||
std::swap(_domainMinimumHeight, _domainMaximumHeight);
|
||||
}
|
||||
|
||||
qCDebug(avatars) << "This domain requires a minimum avatar scale of" << _domainMinimumScale
|
||||
<< "and a maximum avatar scale of" << _domainMaximumScale;
|
||||
qCDebug(avatars) << "This domain requires a minimum avatar height of" << _domainMinimumHeight
|
||||
<< "and a maximum avatar height of" << _domainMaximumHeight;
|
||||
|
||||
const QString AVATAR_WHITELIST_DEFAULT{ "" };
|
||||
static const QString AVATAR_WHITELIST_OPTION = "avatar_whitelist";
|
||||
|
|
|
@ -90,8 +90,8 @@ private:
|
|||
|
||||
float _maxKbpsPerNode = 0.0f;
|
||||
|
||||
float _domainMinimumScale { MIN_AVATAR_SCALE };
|
||||
float _domainMaximumScale { MAX_AVATAR_SCALE };
|
||||
float _domainMinimumHeight { MIN_AVATAR_HEIGHT };
|
||||
float _domainMaximumHeight { MAX_AVATAR_HEIGHT };
|
||||
|
||||
RateCounter<> _broadcastRate;
|
||||
p_high_resolution_clock::time_point _lastDebugMessage;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": 2.0,
|
||||
"version": 2.1,
|
||||
"settings": [
|
||||
{
|
||||
"name": "label",
|
||||
|
@ -1007,20 +1007,20 @@
|
|||
"assignment-types": [ 1, 2 ],
|
||||
"settings": [
|
||||
{
|
||||
"name": "min_avatar_scale",
|
||||
"name": "min_avatar_height",
|
||||
"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
|
||||
"label": "Minimum Avatar Height (meters)",
|
||||
"help": "Limits the height of avatars in your domain. Must be at least 0.009.",
|
||||
"placeholder": 0.4,
|
||||
"default": 0.4
|
||||
},
|
||||
{
|
||||
"name": "max_avatar_scale",
|
||||
"name": "max_avatar_height",
|
||||
"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
|
||||
"label": "Maximum Avatar Height (meters)",
|
||||
"help": "Limits the scale of avatars in your domain. Cannot be greater than 1755.",
|
||||
"placeholder": 5.2,
|
||||
"default": 5.2
|
||||
},
|
||||
{
|
||||
"name": "avatar_whitelist",
|
||||
|
|
|
@ -304,6 +304,26 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
|
|||
|
||||
*wizardCompletedOnce = QVariant(true);
|
||||
}
|
||||
if (oldVersion < 2.1) {
|
||||
// convert old avatar scale settings into avatar height.
|
||||
|
||||
const QString AVATAR_MIN_SCALE_KEYPATH = "avatars.min_avatar_scale";
|
||||
const QString AVATAR_MAX_SCALE_KEYPATH = "avatars.max_avatar_scale";
|
||||
const QString AVATAR_MIN_HEIGHT_KEYPATH = "avatars.min_avatar_height";
|
||||
const QString AVATAR_MAX_HEIGHT_KEYPATH = "avatars.max_avatar_height";
|
||||
|
||||
QVariant* avatarMinScale = _configMap.valueForKeyPath(AVATAR_MIN_SCALE_KEYPATH);
|
||||
if (avatarMinScale) {
|
||||
float scale = avatarMinScale->toFloat();
|
||||
QVariant* avatarMinHeight = _configMap.valueForKeyPath(AVATAR_MIN_HEIGHT_KEYPATH, scale * DEFAULT_AVATAR_HEIGHT);
|
||||
}
|
||||
|
||||
QVariant* avatarMaxScale = _configMap.valueForKeyPath(AVATAR_MAX_SCALE_KEYPATH);
|
||||
if (avatarMaxScale) {
|
||||
float scale = avatarMaxScale->toFloat();
|
||||
QVariant* avatarMinHeight = _configMap.valueForKeyPath(AVATAR_MAX_HEIGHT_KEYPATH, scale * DEFAULT_AVATAR_HEIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
// write the current description version to our settings
|
||||
*versionVariant = _descriptionVersion;
|
||||
|
|
|
@ -2792,10 +2792,10 @@ static int getEventQueueSize(QThread* thread) {
|
|||
static void dumpEventQueue(QThread* thread) {
|
||||
auto threadData = QThreadData::get2(thread);
|
||||
QMutexLocker locker(&threadData->postEventList.mutex);
|
||||
qDebug() << "AJT: event list, size =" << threadData->postEventList.size();
|
||||
qDebug() << "Event list, size =" << threadData->postEventList.size();
|
||||
for (auto& postEvent : threadData->postEventList) {
|
||||
QEvent::Type type = (postEvent.event ? postEvent.event->type() : QEvent::None);
|
||||
qDebug() << "AJT: " << type;
|
||||
qDebug() << " " << type;
|
||||
}
|
||||
}
|
||||
#endif // DEBUG_EVENT_QUEUE
|
||||
|
|
|
@ -1799,6 +1799,7 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) {
|
|||
_skeletonModel->setCauterizeBoneSet(_headBoneSet);
|
||||
_fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl();
|
||||
initAnimGraph();
|
||||
_isAnimatingScale = true;
|
||||
}
|
||||
|
||||
if (_enableDebugDrawDefaultPose || _enableDebugDrawAnimPose) {
|
||||
|
@ -2161,39 +2162,14 @@ bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float
|
|||
// 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",
|
||||
(double)clampedTargetScale, (double)_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", (double)_targetScale);
|
||||
emit(scaleChanged());
|
||||
}
|
||||
|
||||
float MyAvatar::getDomainMinScale() {
|
||||
return _domainMinimumScale;
|
||||
const float unscaledHeight = getUnscaledEyeHeight();
|
||||
return _domainMinimumHeight / unscaledHeight;
|
||||
}
|
||||
|
||||
float MyAvatar::getDomainMaxScale() {
|
||||
return _domainMaximumScale;
|
||||
const float unscaledHeight = getUnscaledEyeHeight();
|
||||
return _domainMaximumHeight / unscaledHeight;
|
||||
}
|
||||
|
||||
void MyAvatar::setGravity(float gravity) {
|
||||
|
@ -2205,70 +2181,54 @@ float MyAvatar::getGravity() {
|
|||
}
|
||||
|
||||
void MyAvatar::increaseSize() {
|
||||
// make sure we're starting from an allowable scale
|
||||
clampTargetScaleToDomainLimits();
|
||||
float minScale = getDomainMinScale();
|
||||
float maxScale = getDomainMaxScale();
|
||||
|
||||
// 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);
|
||||
float newTargetScale = glm::clamp(_targetScale * (1.0f + SCALING_RATIO), minScale, maxScale);
|
||||
setTargetScale(newTargetScale);
|
||||
}
|
||||
|
||||
void MyAvatar::decreaseSize() {
|
||||
// make sure we're starting from an allowable scale
|
||||
clampTargetScaleToDomainLimits();
|
||||
float minScale = getDomainMinScale();
|
||||
float maxScale = getDomainMaxScale();
|
||||
|
||||
// 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);
|
||||
float newTargetScale = glm::clamp(_targetScale * (1.0f - SCALING_RATIO), minScale, maxScale);
|
||||
setTargetScale(newTargetScale);
|
||||
}
|
||||
|
||||
void MyAvatar::resetSize() {
|
||||
// attempt to reset avatar size to the default (clamped to domain limits)
|
||||
const float DEFAULT_AVATAR_SCALE = 1.0f;
|
||||
|
||||
clampScaleChangeToDomainLimits(DEFAULT_AVATAR_SCALE);
|
||||
setTargetScale(DEFAULT_AVATAR_SCALE);
|
||||
}
|
||||
|
||||
void MyAvatar::restrictScaleFromDomainSettings(const QJsonObject& domainSettingsObject) {
|
||||
// pull out the minimum and maximum scale and set them to restrict our scale
|
||||
// pull out the minimum and maximum height 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 MIN_HEIGHT_OPTION = "min_avatar_height";
|
||||
float settingMinHeight = avatarsObject[MIN_HEIGHT_OPTION].toDouble(MIN_AVATAR_HEIGHT);
|
||||
setDomainMinimumHeight(settingMinHeight);
|
||||
|
||||
static const QString MAX_SCALE_OPTION = "max_avatar_scale";
|
||||
float settingMaxScale = avatarsObject[MAX_SCALE_OPTION].toDouble(MAX_AVATAR_SCALE);
|
||||
setDomainMaximumScale(settingMaxScale);
|
||||
static const QString MAX_HEIGHT_OPTION = "max_avatar_height";
|
||||
float settingMaxHeight = avatarsObject[MAX_HEIGHT_OPTION].toDouble(MAX_AVATAR_HEIGHT);
|
||||
setDomainMaximumHeight(settingMaxHeight);
|
||||
|
||||
// make sure that the domain owner didn't flip min and max
|
||||
if (_domainMinimumScale > _domainMaximumScale) {
|
||||
std::swap(_domainMinimumScale, _domainMaximumScale);
|
||||
if (_domainMinimumHeight > _domainMaximumHeight) {
|
||||
std::swap(_domainMinimumHeight, _domainMaximumHeight);
|
||||
}
|
||||
// Set avatar current scale
|
||||
Settings settings;
|
||||
settings.beginGroup("Avatar");
|
||||
_targetScale = loadSetting(settings, "scale", 1.0f);
|
||||
|
||||
qCDebug(interfaceapp) << "This domain requires a minimum avatar scale of " << _domainMinimumScale
|
||||
<< " and a maximum avatar scale of " << _domainMaximumScale
|
||||
<< ". Current avatar scale is " << _targetScale;
|
||||
qCDebug(interfaceapp) << "This domain requires a minimum avatar scale of " << _domainMinimumHeight
|
||||
<< " and a maximum avatar scale of " << _domainMaximumHeight;
|
||||
|
||||
// debug to log if this avatar's scale in this domain will be clamped
|
||||
float clampedScale = glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale);
|
||||
|
||||
if (_targetScale != clampedScale) {
|
||||
qCDebug(interfaceapp) << "Current avatar scale is clamped to " << clampedScale
|
||||
<< " because " << _targetScale << " is not allowed by current domain";
|
||||
// The current scale of avatar should not be more than domain's max_avatar_scale and not less than domain's min_avatar_scale .
|
||||
_targetScale = clampedScale;
|
||||
}
|
||||
_isAnimatingScale = true;
|
||||
|
||||
setModelScale(_targetScale);
|
||||
rebuildCollisionShape();
|
||||
|
@ -2288,8 +2248,8 @@ void MyAvatar::saveAvatarScale() {
|
|||
}
|
||||
|
||||
void MyAvatar::clearScaleRestriction() {
|
||||
_domainMinimumScale = MIN_AVATAR_SCALE;
|
||||
_domainMaximumScale = MAX_AVATAR_SCALE;
|
||||
_domainMinimumHeight = MIN_AVATAR_HEIGHT;
|
||||
_domainMaximumHeight = MAX_AVATAR_HEIGHT;
|
||||
}
|
||||
|
||||
void MyAvatar::goToLocation(const QVariant& propertiesVar) {
|
||||
|
@ -3248,6 +3208,7 @@ void MyAvatar::setModelScale(float scale) {
|
|||
if (changed) {
|
||||
float sensorToWorldScale = getEyeHeight() / getUserEyeHeight();
|
||||
emit sensorToWorldScaleChanged(sensorToWorldScale);
|
||||
emit scaleChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -231,6 +231,9 @@ public:
|
|||
|
||||
const glm::mat4& getGeometryToRigTransform() const { return _geometryToRigTransform; }
|
||||
|
||||
const AnimPose& getModelOffsetPose() const { return _modelOffset; }
|
||||
const AnimPose& getGeometryOffsetPose() const { return _geometryOffset; }
|
||||
|
||||
void setEnableDebugDrawIKTargets(bool enableDebugDrawIKTargets) { _enableDebugDrawIKTargets = enableDebugDrawIKTargets; }
|
||||
void setEnableDebugDrawIKConstraints(bool enableDebugDrawIKConstraints) { _enableDebugDrawIKConstraints = enableDebugDrawIKConstraints; }
|
||||
void setEnableDebugDrawIKChains(bool enableDebugDrawIKChains) { _enableDebugDrawIKChains = enableDebugDrawIKChains; }
|
||||
|
|
|
@ -162,6 +162,7 @@ AABox Avatar::getBounds() const {
|
|||
}
|
||||
|
||||
void Avatar::animateScaleChanges(float deltaTime) {
|
||||
|
||||
if (_isAnimatingScale) {
|
||||
float currentScale = getModelScale();
|
||||
float desiredScale = getDomainLimitedScale();
|
||||
|
@ -172,7 +173,7 @@ void Avatar::animateScaleChanges(float deltaTime) {
|
|||
float animatedScale = (1.0f - blendFactor) * currentScale + blendFactor * desiredScale;
|
||||
|
||||
// snap to the end when we get close enough
|
||||
const float MIN_RELATIVE_ERROR = 0.03f;
|
||||
const float MIN_RELATIVE_ERROR = 0.001f;
|
||||
float relativeError = fabsf(desiredScale - currentScale) / desiredScale;
|
||||
if (relativeError < MIN_RELATIVE_ERROR) {
|
||||
animatedScale = desiredScale;
|
||||
|
@ -698,6 +699,7 @@ void Avatar::fixupModelsInScene(const render::ScenePointer& scene) {
|
|||
_skeletonModel->removeFromScene(scene, transaction);
|
||||
_skeletonModel->addToScene(scene, transaction);
|
||||
canTryFade = true;
|
||||
_isAnimatingScale = true;
|
||||
}
|
||||
for (auto attachmentModel : _attachmentModels) {
|
||||
if (attachmentModel->isRenderable() && attachmentModel->needsFixupInScene()) {
|
||||
|
@ -1195,6 +1197,8 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
|||
void Avatar::setModelURLFinished(bool success) {
|
||||
invalidateJointIndicesCache();
|
||||
|
||||
_isAnimatingScale = true;
|
||||
|
||||
if (!success && _skeletonModelURL != AvatarData::defaultFullAvatarModelUrl()) {
|
||||
const int MAX_SKELETON_DOWNLOAD_ATTEMPTS = 4; // NOTE: we don't want to be as generous as ResourceCache is, we only want 4 attempts
|
||||
if (_skeletonModel->getResourceDownloadAttemptsRemaining() <= 0 ||
|
||||
|
@ -1588,45 +1592,80 @@ float Avatar::getEyeHeight() const {
|
|||
return result;
|
||||
}
|
||||
|
||||
return getModelScale() * getUnscaledEyeHeight();
|
||||
}
|
||||
|
||||
float Avatar::getUnscaledEyeHeight() const {
|
||||
float skeletonHeight = getUnscaledEyeHeightFromSkeleton();
|
||||
|
||||
// Sanity check by looking at the model extents.
|
||||
Extents meshExtents = _skeletonModel->getUnscaledMeshExtents();
|
||||
float meshHeight = meshExtents.size().y;
|
||||
|
||||
// if we determine the mesh is much larger then the skeleton, then we use the mesh height instead.
|
||||
// This helps prevent absurdly large avatars from exceeding the domain height limit.
|
||||
const float MESH_SLOP_RATIO = 1.5;
|
||||
if (meshHeight > skeletonHeight * MESH_SLOP_RATIO) {
|
||||
return meshHeight;
|
||||
} else {
|
||||
return skeletonHeight;
|
||||
}
|
||||
}
|
||||
|
||||
float Avatar::getUnscaledEyeHeightFromSkeleton() const {
|
||||
|
||||
// TODO: if performance becomes a concern we can cache this value rather then computing it everytime.
|
||||
// Makes assumption that the y = 0 plane in geometry is the ground plane.
|
||||
// We also make that assumption in Rig::computeAvatarBoundingCapsule()
|
||||
float avatarScale = getModelScale();
|
||||
|
||||
if (_skeletonModel) {
|
||||
auto& rig = _skeletonModel->getRig();
|
||||
|
||||
// Normally the model offset transform will contain the avatar scale factor, we explicitly remove it here.
|
||||
AnimPose modelOffsetWithoutAvatarScale(glm::vec3(1.0f), rig.getModelOffsetPose().rot(), rig.getModelOffsetPose().trans());
|
||||
AnimPose geomToRigWithoutAvatarScale = modelOffsetWithoutAvatarScale * rig.getGeometryOffsetPose();
|
||||
|
||||
// This factor can be used to scale distances in the geometry frame into the unscaled rig frame.
|
||||
// Typically it will be the unit conversion from cm to m.
|
||||
float scaleFactor = geomToRigWithoutAvatarScale.scale().x; // in practice this always a uniform scale factor.
|
||||
|
||||
int headTopJoint = rig.indexOfJoint("HeadTop_End");
|
||||
int headJoint = rig.indexOfJoint("Head");
|
||||
int eyeJoint = rig.indexOfJoint("LeftEye") != -1 ? rig.indexOfJoint("LeftEye") : rig.indexOfJoint("RightEye");
|
||||
int toeJoint = rig.indexOfJoint("LeftToeBase") != -1 ? rig.indexOfJoint("LeftToeBase") : rig.indexOfJoint("RightToeBase");
|
||||
|
||||
// Makes assumption that the y = 0 plane in geometry is the ground plane.
|
||||
// We also make that assumption in Rig::computeAvatarBoundingCapsule()
|
||||
const float GROUND_Y = 0.0f;
|
||||
|
||||
// Values from the skeleton are in the geometry coordinate frame.
|
||||
auto skeleton = rig.getAnimSkeleton();
|
||||
if (eyeJoint >= 0 && toeJoint >= 0) {
|
||||
// measure from eyes to toes.
|
||||
float eyeHeight = rig.getAbsoluteDefaultPose(eyeJoint).trans().y - rig.getAbsoluteDefaultPose(toeJoint).trans().y;
|
||||
return eyeHeight;
|
||||
// Measure from eyes to toes.
|
||||
float eyeHeight = skeleton->getAbsoluteDefaultPose(eyeJoint).trans().y - skeleton->getAbsoluteDefaultPose(toeJoint).trans().y;
|
||||
return scaleFactor * eyeHeight;
|
||||
} else if (eyeJoint >= 0) {
|
||||
// measure eyes to y = 0 plane.
|
||||
float groundHeight = transformPoint(rig.getGeometryToRigTransform(), glm::vec3(0.0f)).y;
|
||||
float eyeHeight = rig.getAbsoluteDefaultPose(eyeJoint).trans().y - groundHeight;
|
||||
return eyeHeight;
|
||||
// Measure Eye joint to y = 0 plane.
|
||||
float eyeHeight = skeleton->getAbsoluteDefaultPose(eyeJoint).trans().y - GROUND_Y;
|
||||
return scaleFactor * eyeHeight;
|
||||
} else if (headTopJoint >= 0 && toeJoint >= 0) {
|
||||
// measure toe to top of head. Note: default poses already include avatar scale factor
|
||||
// Measure from ToeBase joint to HeadTop_End joint, then remove forehead distance.
|
||||
const float ratio = DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD / DEFAULT_AVATAR_HEIGHT;
|
||||
float height = rig.getAbsoluteDefaultPose(headTopJoint).trans().y - rig.getAbsoluteDefaultPose(toeJoint).trans().y;
|
||||
return height - height * ratio;
|
||||
float height = skeleton->getAbsoluteDefaultPose(headTopJoint).trans().y - skeleton->getAbsoluteDefaultPose(toeJoint).trans().y;
|
||||
return scaleFactor * (height - height * ratio);
|
||||
} else if (headTopJoint >= 0) {
|
||||
// Measure from HeadTop_End joint to the ground, then remove forehead distance.
|
||||
const float ratio = DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD / DEFAULT_AVATAR_HEIGHT;
|
||||
float groundHeight = transformPoint(rig.getGeometryToRigTransform(), glm::vec3(0.0f)).y;
|
||||
float headHeight = rig.getAbsoluteDefaultPose(headTopJoint).trans().y - groundHeight;
|
||||
return headHeight - headHeight * ratio;
|
||||
float headHeight = skeleton->getAbsoluteDefaultPose(headTopJoint).trans().y - GROUND_Y;
|
||||
return scaleFactor * (headHeight - headHeight * ratio);
|
||||
} else if (headJoint >= 0) {
|
||||
float groundHeight = transformPoint(rig.getGeometryToRigTransform(), glm::vec3(0.0f)).y;
|
||||
// Measure Head joint to the ground, then add in distance from neck to eye.
|
||||
const float DEFAULT_AVATAR_NECK_TO_EYE = DEFAULT_AVATAR_NECK_TO_TOP_OF_HEAD - DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD;
|
||||
const float ratio = DEFAULT_AVATAR_NECK_TO_EYE / DEFAULT_AVATAR_NECK_HEIGHT;
|
||||
float neckHeight = rig.getAbsoluteDefaultPose(headJoint).trans().y - groundHeight;
|
||||
return neckHeight + neckHeight * ratio;
|
||||
float neckHeight = skeleton->getAbsoluteDefaultPose(headJoint).trans().y - GROUND_Y;
|
||||
return scaleFactor * (neckHeight + neckHeight * ratio);
|
||||
} else {
|
||||
return avatarScale * DEFAULT_AVATAR_EYE_HEIGHT;
|
||||
return DEFAULT_AVATAR_EYE_HEIGHT;
|
||||
}
|
||||
} else {
|
||||
return avatarScale * DEFAULT_AVATAR_EYE_HEIGHT;
|
||||
return DEFAULT_AVATAR_EYE_HEIGHT;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -255,12 +255,16 @@ public:
|
|||
bool isFading() const { return _isFading; }
|
||||
void updateFadingStatus(render::ScenePointer scene);
|
||||
|
||||
/**jsdoc
|
||||
* Provides read only access to the current eye height of the avatar.
|
||||
* @function Avatar.getEyeHeight
|
||||
* @returns {number} eye height of avatar in meters
|
||||
*/
|
||||
Q_INVOKABLE float getEyeHeight() const;
|
||||
Q_INVOKABLE virtual float getEyeHeight() const override;
|
||||
|
||||
// returns eye height of avatar in meters, ignoreing avatar scale.
|
||||
// if _targetScale is 1 then this will be identical to getEyeHeight;
|
||||
virtual float getUnscaledEyeHeight() const override;
|
||||
|
||||
// returns true, if an acurate eye height estimage can be obtained by inspecting the avatar model skeleton and geometry,
|
||||
// not all subclasses of AvatarData have access to this data.
|
||||
virtual bool canMeasureEyeHeight() const override { return true; }
|
||||
|
||||
|
||||
virtual float getModelScale() const { return _modelScale; }
|
||||
virtual void setModelScale(float scale) { _modelScale = scale; }
|
||||
|
@ -279,6 +283,7 @@ public slots:
|
|||
void setModelURLFinished(bool success);
|
||||
|
||||
protected:
|
||||
float Avatar::getUnscaledEyeHeightFromSkeleton() const;
|
||||
virtual const QString& getSessionDisplayNameForTransport() const override { return _empty; } // Save a tiny bit of bandwidth. Mixer won't look at what we send.
|
||||
QString _empty{};
|
||||
virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) override { _sessionDisplayName = sessionDisplayName; } // don't use no-op setter!
|
||||
|
@ -349,7 +354,7 @@ protected:
|
|||
RateCounter<> _skeletonModelSimulationRate;
|
||||
RateCounter<> _jointDataSimulationRate;
|
||||
|
||||
private:
|
||||
protected:
|
||||
class AvatarEntityDataHash {
|
||||
public:
|
||||
AvatarEntityDataHash(uint32_t h) : hash(h) {};
|
||||
|
|
|
@ -117,6 +117,37 @@ void AvatarData::setTargetScale(float targetScale) {
|
|||
}
|
||||
}
|
||||
|
||||
float AvatarData::getDomainLimitedScale() const {
|
||||
if (canMeasureEyeHeight()) {
|
||||
const float unscaledEyeHeight = getUnscaledEyeHeight();
|
||||
|
||||
// Add in an estimate of forehead height.
|
||||
const float ratio = unscaledEyeHeight / DEFAULT_AVATAR_HEIGHT;
|
||||
const float unscaledHeight = unscaledEyeHeight + ratio * DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD;
|
||||
|
||||
const float minScale = _domainMinimumHeight / unscaledHeight;
|
||||
const float maxScale = _domainMaximumHeight / unscaledHeight;
|
||||
return glm::clamp(_targetScale, minScale, maxScale);
|
||||
} else {
|
||||
// We can't make a good estimate.
|
||||
return _targetScale;
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarData::setDomainMinimumHeight(float domainMinimumHeight) {
|
||||
_domainMinimumHeight = glm::clamp(domainMinimumHeight, MIN_AVATAR_HEIGHT, MAX_AVATAR_HEIGHT);
|
||||
}
|
||||
|
||||
void AvatarData::setDomainMaximumHeight(float domainMaximumHeight) {
|
||||
_domainMaximumHeight = glm::clamp(domainMaximumHeight, MIN_AVATAR_HEIGHT, MAX_AVATAR_HEIGHT);
|
||||
}
|
||||
|
||||
float AvatarData::getHeight() const {
|
||||
const float eyeHeight = getEyeHeight();
|
||||
const float ratio = eyeHeight / DEFAULT_AVATAR_HEIGHT;
|
||||
return eyeHeight + ratio * DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD;
|
||||
}
|
||||
|
||||
glm::vec3 AvatarData::getHandPosition() const {
|
||||
return getOrientation() * _handPosition + getPosition();
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <QtScript/QScriptValueIterator>
|
||||
#include <QReadWriteLock>
|
||||
|
||||
#include <AvatarConstants.h>
|
||||
#include <JointData.h>
|
||||
#include <NLPacket.h>
|
||||
#include <Node.h>
|
||||
|
@ -257,9 +258,6 @@ namespace AvatarDataPacket {
|
|||
size_t maxJointDataSize(size_t numJoints);
|
||||
}
|
||||
|
||||
static const float MAX_AVATAR_SCALE = 1000.0f;
|
||||
static const float MIN_AVATAR_SCALE = .005f;
|
||||
|
||||
const float MAX_AUDIO_LOUDNESS = 1000.0f; // close enough for mouth animation
|
||||
|
||||
const int AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS = 1000;
|
||||
|
@ -484,12 +482,34 @@ public:
|
|||
// Scale
|
||||
virtual void setTargetScale(float targetScale);
|
||||
|
||||
float getDomainLimitedScale() const { return glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale); }
|
||||
float getDomainLimitedScale() const;
|
||||
|
||||
void setDomainMinimumScale(float domainMinimumScale)
|
||||
{ _domainMinimumScale = glm::clamp(domainMinimumScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); _scaleChanged = usecTimestampNow(); }
|
||||
void setDomainMaximumScale(float domainMaximumScale)
|
||||
{ _domainMaximumScale = glm::clamp(domainMaximumScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); _scaleChanged = usecTimestampNow(); }
|
||||
// returns eye height of avatar in meters, ignoreing avatar scale.
|
||||
// if _targetScale is 1 then this will be identical to getEyeHeight;
|
||||
virtual float getUnscaledEyeHeight() const { return DEFAULT_AVATAR_EYE_HEIGHT; }
|
||||
|
||||
// returns true, if an acurate eye height estimage can be obtained by inspecting the avatar model skeleton and geometry,
|
||||
// not all subclasses of AvatarData have access to this data.
|
||||
virtual bool canMeasureEyeHeight() const { return false; }
|
||||
|
||||
/**jsdoc
|
||||
* Provides read only access to the current eye height of the avatar.
|
||||
* This height is only an estimate and might be incorrect for avatars that are missing standard joints.
|
||||
* @function AvatarData.getEyeHeight
|
||||
* @returns {number} eye height of avatar in meters
|
||||
*/
|
||||
Q_INVOKABLE virtual float getEyeHeight() const { return _targetScale * getUnscaledEyeHeight(); }
|
||||
|
||||
/**jsdoc
|
||||
* Provides read only access to the current height of the avatar.
|
||||
* This height is only an estimate and might be incorrect for avatars that are missing standard joints.
|
||||
* @function AvatarData.getHeight
|
||||
* @returns {number} height of avatar in meters
|
||||
*/
|
||||
Q_INVOKABLE virtual float getHeight() const;
|
||||
|
||||
void setDomainMinimumHeight(float domainMinimumHeight);
|
||||
void setDomainMaximumHeight(float domainMaximumHeight);
|
||||
|
||||
// Hand State
|
||||
Q_INVOKABLE void setHandState(char s) { _handState = s; }
|
||||
|
@ -706,8 +726,8 @@ protected:
|
|||
|
||||
// Body scale
|
||||
float _targetScale;
|
||||
float _domainMinimumScale { MIN_AVATAR_SCALE };
|
||||
float _domainMaximumScale { MAX_AVATAR_SCALE };
|
||||
float _domainMinimumHeight { MIN_AVATAR_HEIGHT };
|
||||
float _domainMaximumHeight { MAX_AVATAR_HEIGHT };
|
||||
|
||||
// Hand state (are we grabbing something or not)
|
||||
char _handState;
|
||||
|
|
|
@ -74,6 +74,8 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
return static_cast<PacketVersion>(AudioVersion::HighDynamicRangeVolume);
|
||||
case PacketType::ICEPing:
|
||||
return static_cast<PacketVersion>(IcePingVersion::SendICEPeerID);
|
||||
case PacketType::DomainSettings:
|
||||
return 18; // replace min_avatar_scale and max_avatar_scale with min_avatar_height and max_avatar_height
|
||||
default:
|
||||
return 17;
|
||||
}
|
||||
|
|
|
@ -163,7 +163,7 @@ void Model::setScale(const glm::vec3& scale) {
|
|||
_scaledToFit = false;
|
||||
}
|
||||
|
||||
const float SCALE_CHANGE_EPSILON = 0.01f;
|
||||
const float SCALE_CHANGE_EPSILON = 0.001f;
|
||||
|
||||
void Model::setScaleInternal(const glm::vec3& scale) {
|
||||
if (glm::distance(_scale, scale) > SCALE_CHANGE_EPSILON) {
|
||||
|
|
|
@ -204,6 +204,9 @@ public:
|
|||
/// Returns the extents of the model's mesh
|
||||
Extents getMeshExtents() const;
|
||||
|
||||
/// Returns the unscaled extents of the model's mesh
|
||||
Extents getUnscaledMeshExtents() const;
|
||||
|
||||
void setTranslation(const glm::vec3& translation);
|
||||
void setRotation(const glm::quat& rotation);
|
||||
void setTransformNoUpdateRenderItems(const Transform& transform); // temporary HACK
|
||||
|
@ -276,9 +279,6 @@ protected:
|
|||
void setBlendshapeCoefficients(const QVector<float>& coefficients) { _blendshapeCoefficients = coefficients; }
|
||||
const QVector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
|
||||
|
||||
/// Returns the unscaled extents of the model's mesh
|
||||
Extents getUnscaledMeshExtents() const;
|
||||
|
||||
/// Clear the joint states
|
||||
void clearJointState(int index);
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#ifndef hifi_AvatarConstants_h
|
||||
#define hifi_AvatarConstants_h
|
||||
|
||||
#include "GLMHelpers.h"
|
||||
|
||||
// 50th Percentile Man
|
||||
const float DEFAULT_AVATAR_HEIGHT = 1.755f; // meters
|
||||
const float DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD = 0.11f; // meters
|
||||
|
@ -52,5 +54,10 @@ const float DEFAULT_AVATAR_JUMP_HEIGHT = (DEFAULT_AVATAR_JUMP_SPEED * DEFAULT_AV
|
|||
const float DEFAULT_AVATAR_FALL_HEIGHT = 20.0f; // meters
|
||||
const float DEFAULT_AVATAR_MIN_HOVER_HEIGHT = 2.5f; // meters
|
||||
|
||||
static const float MAX_AVATAR_SCALE = 1000.0f;
|
||||
static const float MIN_AVATAR_SCALE = 0.005f;
|
||||
|
||||
static const float MAX_AVATAR_HEIGHT = 1000.0f * DEFAULT_AVATAR_HEIGHT; // meters
|
||||
static const float MIN_AVATAR_HEIGHT = 0.005f * DEFAULT_AVATAR_HEIGHT; // meters
|
||||
|
||||
#endif // hifi_AvatarConstants_h
|
||||
|
|
Loading…
Reference in a new issue