mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-09 08:36:26 +02:00
Merge pull request #11865 from hyperlogic/feature/domain-limited-height
domain uses min/max avatar height instead of min/max scale
This commit is contained in:
commit
4691a98509
16 changed files with 246 additions and 144 deletions
|
@ -870,8 +870,8 @@ AvatarMixerClientData* AvatarMixer::getOrCreateClientData(SharedNodePointer node
|
||||||
node->setLinkedData(std::unique_ptr<NodeData> { new AvatarMixerClientData(node->getUUID()) });
|
node->setLinkedData(std::unique_ptr<NodeData> { new AvatarMixerClientData(node->getUUID()) });
|
||||||
clientData = dynamic_cast<AvatarMixerClientData*>(node->getLinkedData());
|
clientData = dynamic_cast<AvatarMixerClientData*>(node->getLinkedData());
|
||||||
auto& avatar = clientData->getAvatar();
|
auto& avatar = clientData->getAvatar();
|
||||||
avatar.setDomainMinimumScale(_domainMinimumScale);
|
avatar.setDomainMinimumHeight(_domainMinimumHeight);
|
||||||
avatar.setDomainMaximumScale(_domainMaximumScale);
|
avatar.setDomainMaximumHeight(_domainMaximumHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
return clientData;
|
return clientData;
|
||||||
|
@ -939,21 +939,21 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) {
|
||||||
|
|
||||||
const QString AVATARS_SETTINGS_KEY = "avatars";
|
const QString AVATARS_SETTINGS_KEY = "avatars";
|
||||||
|
|
||||||
static const QString MIN_SCALE_OPTION = "min_avatar_scale";
|
static const QString MIN_HEIGHT_OPTION = "min_avatar_height";
|
||||||
float settingMinScale = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MIN_SCALE_OPTION].toDouble(MIN_AVATAR_SCALE);
|
float settingMinHeight = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MIN_HEIGHT_OPTION].toDouble(MIN_AVATAR_HEIGHT);
|
||||||
_domainMinimumScale = glm::clamp(settingMinScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE);
|
_domainMinimumHeight = glm::clamp(settingMinHeight, MIN_AVATAR_HEIGHT, MAX_AVATAR_HEIGHT);
|
||||||
|
|
||||||
static const QString MAX_SCALE_OPTION = "max_avatar_scale";
|
static const QString MAX_HEIGHT_OPTION = "max_avatar_height";
|
||||||
float settingMaxScale = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MAX_SCALE_OPTION].toDouble(MAX_AVATAR_SCALE);
|
float settingMaxHeight = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MAX_HEIGHT_OPTION].toDouble(MAX_AVATAR_HEIGHT);
|
||||||
_domainMaximumScale = glm::clamp(settingMaxScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE);
|
_domainMaximumHeight = glm::clamp(settingMaxHeight, MIN_AVATAR_HEIGHT, MAX_AVATAR_HEIGHT);
|
||||||
|
|
||||||
// make sure that the domain owner didn't flip min and max
|
// make sure that the domain owner didn't flip min and max
|
||||||
if (_domainMinimumScale > _domainMaximumScale) {
|
if (_domainMinimumHeight > _domainMaximumHeight) {
|
||||||
std::swap(_domainMinimumScale, _domainMaximumScale);
|
std::swap(_domainMinimumHeight, _domainMaximumHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
qCDebug(avatars) << "This domain requires a minimum avatar scale of" << _domainMinimumScale
|
qCDebug(avatars) << "This domain requires a minimum avatar height of" << _domainMinimumHeight
|
||||||
<< "and a maximum avatar scale of" << _domainMaximumScale;
|
<< "and a maximum avatar height of" << _domainMaximumHeight;
|
||||||
|
|
||||||
const QString AVATAR_WHITELIST_DEFAULT{ "" };
|
const QString AVATAR_WHITELIST_DEFAULT{ "" };
|
||||||
static const QString AVATAR_WHITELIST_OPTION = "avatar_whitelist";
|
static const QString AVATAR_WHITELIST_OPTION = "avatar_whitelist";
|
||||||
|
|
|
@ -90,8 +90,8 @@ private:
|
||||||
|
|
||||||
float _maxKbpsPerNode = 0.0f;
|
float _maxKbpsPerNode = 0.0f;
|
||||||
|
|
||||||
float _domainMinimumScale { MIN_AVATAR_SCALE };
|
float _domainMinimumHeight { MIN_AVATAR_HEIGHT };
|
||||||
float _domainMaximumScale { MAX_AVATAR_SCALE };
|
float _domainMaximumHeight { MAX_AVATAR_HEIGHT };
|
||||||
|
|
||||||
RateCounter<> _broadcastRate;
|
RateCounter<> _broadcastRate;
|
||||||
p_high_resolution_clock::time_point _lastDebugMessage;
|
p_high_resolution_clock::time_point _lastDebugMessage;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": 2.0,
|
"version": 2.1,
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
"name": "label",
|
"name": "label",
|
||||||
|
@ -1015,20 +1015,20 @@
|
||||||
"assignment-types": [ 1, 2 ],
|
"assignment-types": [ 1, 2 ],
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
"name": "min_avatar_scale",
|
"name": "min_avatar_height",
|
||||||
"type": "double",
|
"type": "double",
|
||||||
"label": "Minimum Avatar Scale",
|
"label": "Minimum Avatar Height (meters)",
|
||||||
"help": "Limits the scale of avatars in your domain. Must be at least 0.005.",
|
"help": "Limits the height of avatars in your domain. Must be at least 0.009.",
|
||||||
"placeholder": 0.25,
|
"placeholder": 0.4,
|
||||||
"default": 0.25
|
"default": 0.4
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "max_avatar_scale",
|
"name": "max_avatar_height",
|
||||||
"type": "double",
|
"type": "double",
|
||||||
"label": "Maximum Avatar Scale",
|
"label": "Maximum Avatar Height (meters)",
|
||||||
"help": "Limits the scale of avatars in your domain. Cannot be greater than 1000.",
|
"help": "Limits the scale of avatars in your domain. Cannot be greater than 1755.",
|
||||||
"placeholder": 3.0,
|
"placeholder": 5.2,
|
||||||
"default": 3.0
|
"default": 5.2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "avatar_whitelist",
|
"name": "avatar_whitelist",
|
||||||
|
|
|
@ -304,6 +304,26 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
|
||||||
|
|
||||||
*wizardCompletedOnce = QVariant(true);
|
*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();
|
||||||
|
_configMap.valueForKeyPath(AVATAR_MIN_HEIGHT_KEYPATH, scale * DEFAULT_AVATAR_HEIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant* avatarMaxScale = _configMap.valueForKeyPath(AVATAR_MAX_SCALE_KEYPATH);
|
||||||
|
if (avatarMaxScale) {
|
||||||
|
float scale = avatarMaxScale->toFloat();
|
||||||
|
_configMap.valueForKeyPath(AVATAR_MAX_HEIGHT_KEYPATH, scale * DEFAULT_AVATAR_HEIGHT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// write the current description version to our settings
|
// write the current description version to our settings
|
||||||
*versionVariant = _descriptionVersion;
|
*versionVariant = _descriptionVersion;
|
||||||
|
|
|
@ -2812,10 +2812,10 @@ static int getEventQueueSize(QThread* thread) {
|
||||||
static void dumpEventQueue(QThread* thread) {
|
static void dumpEventQueue(QThread* thread) {
|
||||||
auto threadData = QThreadData::get2(thread);
|
auto threadData = QThreadData::get2(thread);
|
||||||
QMutexLocker locker(&threadData->postEventList.mutex);
|
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) {
|
for (auto& postEvent : threadData->postEventList) {
|
||||||
QEvent::Type type = (postEvent.event ? postEvent.event->type() : QEvent::None);
|
QEvent::Type type = (postEvent.event ? postEvent.event->type() : QEvent::None);
|
||||||
qDebug() << "AJT: " << type;
|
qDebug() << " " << type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // DEBUG_EVENT_QUEUE
|
#endif // DEBUG_EVENT_QUEUE
|
||||||
|
|
|
@ -1799,6 +1799,7 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) {
|
||||||
_skeletonModel->setCauterizeBoneSet(_headBoneSet);
|
_skeletonModel->setCauterizeBoneSet(_headBoneSet);
|
||||||
_fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl();
|
_fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl();
|
||||||
initAnimGraph();
|
initAnimGraph();
|
||||||
|
_isAnimatingScale = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_enableDebugDrawDefaultPose || _enableDebugDrawAnimPose) {
|
if (_enableDebugDrawDefaultPose || _enableDebugDrawAnimPose) {
|
||||||
|
@ -2161,41 +2162,6 @@ 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
|
// 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.
|
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
float MyAvatar::getDomainMaxScale() {
|
|
||||||
return _domainMaximumScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MyAvatar::setGravity(float gravity) {
|
void MyAvatar::setGravity(float gravity) {
|
||||||
_characterController.setGravity(gravity);
|
_characterController.setGravity(gravity);
|
||||||
}
|
}
|
||||||
|
@ -2205,70 +2171,58 @@ float MyAvatar::getGravity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::increaseSize() {
|
void MyAvatar::increaseSize() {
|
||||||
// make sure we're starting from an allowable scale
|
float minScale = getDomainMinScale();
|
||||||
clampTargetScaleToDomainLimits();
|
float maxScale = getDomainMaxScale();
|
||||||
|
|
||||||
// calculate what our new scale should be
|
float clampedTargetScale = glm::clamp(_targetScale, minScale, maxScale);
|
||||||
float updatedTargetScale = _targetScale * (1.0f + SCALING_RATIO);
|
float newTargetScale = glm::clamp(clampedTargetScale * (1.0f + SCALING_RATIO), minScale, maxScale);
|
||||||
|
|
||||||
// attempt to change to desired scale (clamped to the domain limits)
|
setTargetScale(newTargetScale);
|
||||||
clampScaleChangeToDomainLimits(updatedTargetScale);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::decreaseSize() {
|
void MyAvatar::decreaseSize() {
|
||||||
// make sure we're starting from an allowable scale
|
float minScale = getDomainMinScale();
|
||||||
clampTargetScaleToDomainLimits();
|
float maxScale = getDomainMaxScale();
|
||||||
|
|
||||||
// calculate what our new scale should be
|
float clampedTargetScale = glm::clamp(_targetScale, minScale, maxScale);
|
||||||
float updatedTargetScale = _targetScale * (1.0f - SCALING_RATIO);
|
float newTargetScale = glm::clamp(clampedTargetScale * (1.0f - SCALING_RATIO), minScale, maxScale);
|
||||||
|
|
||||||
// attempt to change to desired scale (clamped to the domain limits)
|
setTargetScale(newTargetScale);
|
||||||
clampScaleChangeToDomainLimits(updatedTargetScale);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::resetSize() {
|
void MyAvatar::resetSize() {
|
||||||
// attempt to reset avatar size to the default (clamped to domain limits)
|
// attempt to reset avatar size to the default (clamped to domain limits)
|
||||||
const float DEFAULT_AVATAR_SCALE = 1.0f;
|
const float DEFAULT_AVATAR_SCALE = 1.0f;
|
||||||
|
setTargetScale(DEFAULT_AVATAR_SCALE);
|
||||||
clampScaleChangeToDomainLimits(DEFAULT_AVATAR_SCALE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::restrictScaleFromDomainSettings(const QJsonObject& domainSettingsObject) {
|
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";
|
static const QString AVATAR_SETTINGS_KEY = "avatars";
|
||||||
auto avatarsObject = domainSettingsObject[AVATAR_SETTINGS_KEY].toObject();
|
auto avatarsObject = domainSettingsObject[AVATAR_SETTINGS_KEY].toObject();
|
||||||
|
|
||||||
static const QString MIN_SCALE_OPTION = "min_avatar_scale";
|
static const QString MIN_HEIGHT_OPTION = "min_avatar_height";
|
||||||
float settingMinScale = avatarsObject[MIN_SCALE_OPTION].toDouble(MIN_AVATAR_SCALE);
|
float settingMinHeight = avatarsObject[MIN_HEIGHT_OPTION].toDouble(MIN_AVATAR_HEIGHT);
|
||||||
setDomainMinimumScale(settingMinScale);
|
setDomainMinimumHeight(settingMinHeight);
|
||||||
|
|
||||||
static const QString MAX_SCALE_OPTION = "max_avatar_scale";
|
static const QString MAX_HEIGHT_OPTION = "max_avatar_height";
|
||||||
float settingMaxScale = avatarsObject[MAX_SCALE_OPTION].toDouble(MAX_AVATAR_SCALE);
|
float settingMaxHeight = avatarsObject[MAX_HEIGHT_OPTION].toDouble(MAX_AVATAR_HEIGHT);
|
||||||
setDomainMaximumScale(settingMaxScale);
|
setDomainMaximumHeight(settingMaxHeight);
|
||||||
|
|
||||||
// make sure that the domain owner didn't flip min and max
|
// make sure that the domain owner didn't flip min and max
|
||||||
if (_domainMinimumScale > _domainMaximumScale) {
|
if (_domainMinimumHeight > _domainMaximumHeight) {
|
||||||
std::swap(_domainMinimumScale, _domainMaximumScale);
|
std::swap(_domainMinimumHeight, _domainMaximumHeight);
|
||||||
}
|
}
|
||||||
// Set avatar current scale
|
// Set avatar current scale
|
||||||
Settings settings;
|
Settings settings;
|
||||||
settings.beginGroup("Avatar");
|
settings.beginGroup("Avatar");
|
||||||
_targetScale = loadSetting(settings, "scale", 1.0f);
|
_targetScale = loadSetting(settings, "scale", 1.0f);
|
||||||
|
|
||||||
qCDebug(interfaceapp) << "This domain requires a minimum avatar scale of " << _domainMinimumScale
|
qCDebug(interfaceapp) << "This domain requires a minimum avatar scale of " << _domainMinimumHeight
|
||||||
<< " and a maximum avatar scale of " << _domainMaximumScale
|
<< " and a maximum avatar scale of " << _domainMaximumHeight;
|
||||||
<< ". Current avatar scale is " << _targetScale;
|
|
||||||
|
|
||||||
// debug to log if this avatar's scale in this domain will be clamped
|
_isAnimatingScale = true;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
setModelScale(_targetScale);
|
setModelScale(_targetScale);
|
||||||
rebuildCollisionShape();
|
rebuildCollisionShape();
|
||||||
|
@ -2288,8 +2242,8 @@ void MyAvatar::saveAvatarScale() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::clearScaleRestriction() {
|
void MyAvatar::clearScaleRestriction() {
|
||||||
_domainMinimumScale = MIN_AVATAR_SCALE;
|
_domainMinimumHeight = MIN_AVATAR_HEIGHT;
|
||||||
_domainMaximumScale = MAX_AVATAR_SCALE;
|
_domainMaximumHeight = MAX_AVATAR_HEIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::goToLocation(const QVariant& propertiesVar) {
|
void MyAvatar::goToLocation(const QVariant& propertiesVar) {
|
||||||
|
@ -3248,6 +3202,7 @@ void MyAvatar::setModelScale(float scale) {
|
||||||
if (changed) {
|
if (changed) {
|
||||||
float sensorToWorldScale = getEyeHeight() / getUserEyeHeight();
|
float sensorToWorldScale = getEyeHeight() / getUserEyeHeight();
|
||||||
emit sensorToWorldScaleChanged(sensorToWorldScale);
|
emit sensorToWorldScaleChanged(sensorToWorldScale);
|
||||||
|
emit scaleChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -558,8 +558,6 @@ public slots:
|
||||||
void increaseSize();
|
void increaseSize();
|
||||||
void decreaseSize();
|
void decreaseSize();
|
||||||
void resetSize();
|
void resetSize();
|
||||||
float getDomainMinScale();
|
|
||||||
float getDomainMaxScale();
|
|
||||||
|
|
||||||
void setGravity(float gravity);
|
void setGravity(float gravity);
|
||||||
float getGravity();
|
float getGravity();
|
||||||
|
|
|
@ -231,6 +231,9 @@ public:
|
||||||
|
|
||||||
const glm::mat4& getGeometryToRigTransform() const { return _geometryToRigTransform; }
|
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 setEnableDebugDrawIKTargets(bool enableDebugDrawIKTargets) { _enableDebugDrawIKTargets = enableDebugDrawIKTargets; }
|
||||||
void setEnableDebugDrawIKConstraints(bool enableDebugDrawIKConstraints) { _enableDebugDrawIKConstraints = enableDebugDrawIKConstraints; }
|
void setEnableDebugDrawIKConstraints(bool enableDebugDrawIKConstraints) { _enableDebugDrawIKConstraints = enableDebugDrawIKConstraints; }
|
||||||
void setEnableDebugDrawIKChains(bool enableDebugDrawIKChains) { _enableDebugDrawIKChains = enableDebugDrawIKChains; }
|
void setEnableDebugDrawIKChains(bool enableDebugDrawIKChains) { _enableDebugDrawIKChains = enableDebugDrawIKChains; }
|
||||||
|
|
|
@ -162,6 +162,7 @@ AABox Avatar::getBounds() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Avatar::animateScaleChanges(float deltaTime) {
|
void Avatar::animateScaleChanges(float deltaTime) {
|
||||||
|
|
||||||
if (_isAnimatingScale) {
|
if (_isAnimatingScale) {
|
||||||
float currentScale = getModelScale();
|
float currentScale = getModelScale();
|
||||||
float desiredScale = getDomainLimitedScale();
|
float desiredScale = getDomainLimitedScale();
|
||||||
|
@ -172,7 +173,7 @@ void Avatar::animateScaleChanges(float deltaTime) {
|
||||||
float animatedScale = (1.0f - blendFactor) * currentScale + blendFactor * desiredScale;
|
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_ERROR = 0.03f;
|
const float MIN_RELATIVE_ERROR = 0.001f;
|
||||||
float relativeError = fabsf(desiredScale - currentScale) / desiredScale;
|
float relativeError = fabsf(desiredScale - currentScale) / desiredScale;
|
||||||
if (relativeError < MIN_RELATIVE_ERROR) {
|
if (relativeError < MIN_RELATIVE_ERROR) {
|
||||||
animatedScale = desiredScale;
|
animatedScale = desiredScale;
|
||||||
|
@ -698,6 +699,7 @@ void Avatar::fixupModelsInScene(const render::ScenePointer& scene) {
|
||||||
_skeletonModel->removeFromScene(scene, transaction);
|
_skeletonModel->removeFromScene(scene, transaction);
|
||||||
_skeletonModel->addToScene(scene, transaction);
|
_skeletonModel->addToScene(scene, transaction);
|
||||||
canTryFade = true;
|
canTryFade = true;
|
||||||
|
_isAnimatingScale = true;
|
||||||
}
|
}
|
||||||
for (auto attachmentModel : _attachmentModels) {
|
for (auto attachmentModel : _attachmentModels) {
|
||||||
if (attachmentModel->isRenderable() && attachmentModel->needsFixupInScene()) {
|
if (attachmentModel->isRenderable() && attachmentModel->needsFixupInScene()) {
|
||||||
|
@ -1195,6 +1197,8 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
||||||
void Avatar::setModelURLFinished(bool success) {
|
void Avatar::setModelURLFinished(bool success) {
|
||||||
invalidateJointIndicesCache();
|
invalidateJointIndicesCache();
|
||||||
|
|
||||||
|
_isAnimatingScale = true;
|
||||||
|
|
||||||
if (!success && _skeletonModelURL != AvatarData::defaultFullAvatarModelUrl()) {
|
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
|
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 ||
|
if (_skeletonModel->getResourceDownloadAttemptsRemaining() <= 0 ||
|
||||||
|
@ -1588,45 +1592,80 @@ float Avatar::getEyeHeight() const {
|
||||||
return result;
|
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.5f;
|
||||||
|
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.
|
// 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) {
|
if (_skeletonModel) {
|
||||||
auto& rig = _skeletonModel->getRig();
|
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 headTopJoint = rig.indexOfJoint("HeadTop_End");
|
||||||
int headJoint = rig.indexOfJoint("Head");
|
int headJoint = rig.indexOfJoint("Head");
|
||||||
int eyeJoint = rig.indexOfJoint("LeftEye") != -1 ? rig.indexOfJoint("LeftEye") : rig.indexOfJoint("RightEye");
|
int eyeJoint = rig.indexOfJoint("LeftEye") != -1 ? rig.indexOfJoint("LeftEye") : rig.indexOfJoint("RightEye");
|
||||||
int toeJoint = rig.indexOfJoint("LeftToeBase") != -1 ? rig.indexOfJoint("LeftToeBase") : rig.indexOfJoint("RightToeBase");
|
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) {
|
if (eyeJoint >= 0 && toeJoint >= 0) {
|
||||||
// measure from eyes to toes.
|
// Measure from eyes to toes.
|
||||||
float eyeHeight = rig.getAbsoluteDefaultPose(eyeJoint).trans().y - rig.getAbsoluteDefaultPose(toeJoint).trans().y;
|
float eyeHeight = skeleton->getAbsoluteDefaultPose(eyeJoint).trans().y - skeleton->getAbsoluteDefaultPose(toeJoint).trans().y;
|
||||||
return eyeHeight;
|
return scaleFactor * eyeHeight;
|
||||||
} else if (eyeJoint >= 0) {
|
} else if (eyeJoint >= 0) {
|
||||||
// measure eyes to y = 0 plane.
|
// Measure Eye joint to y = 0 plane.
|
||||||
float groundHeight = transformPoint(rig.getGeometryToRigTransform(), glm::vec3(0.0f)).y;
|
float eyeHeight = skeleton->getAbsoluteDefaultPose(eyeJoint).trans().y - GROUND_Y;
|
||||||
float eyeHeight = rig.getAbsoluteDefaultPose(eyeJoint).trans().y - groundHeight;
|
return scaleFactor * eyeHeight;
|
||||||
return eyeHeight;
|
|
||||||
} else if (headTopJoint >= 0 && toeJoint >= 0) {
|
} 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;
|
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;
|
float height = skeleton->getAbsoluteDefaultPose(headTopJoint).trans().y - skeleton->getAbsoluteDefaultPose(toeJoint).trans().y;
|
||||||
return height - height * ratio;
|
return scaleFactor * (height - height * ratio);
|
||||||
} else if (headTopJoint >= 0) {
|
} 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;
|
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 = skeleton->getAbsoluteDefaultPose(headTopJoint).trans().y - GROUND_Y;
|
||||||
float headHeight = rig.getAbsoluteDefaultPose(headTopJoint).trans().y - groundHeight;
|
return scaleFactor * (headHeight - headHeight * ratio);
|
||||||
return headHeight - headHeight * ratio;
|
|
||||||
} else if (headJoint >= 0) {
|
} 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 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;
|
const float ratio = DEFAULT_AVATAR_NECK_TO_EYE / DEFAULT_AVATAR_NECK_HEIGHT;
|
||||||
float neckHeight = rig.getAbsoluteDefaultPose(headJoint).trans().y - groundHeight;
|
float neckHeight = skeleton->getAbsoluteDefaultPose(headJoint).trans().y - GROUND_Y;
|
||||||
return neckHeight + neckHeight * ratio;
|
return scaleFactor * (neckHeight + neckHeight * ratio);
|
||||||
} else {
|
} else {
|
||||||
return avatarScale * DEFAULT_AVATAR_EYE_HEIGHT;
|
return DEFAULT_AVATAR_EYE_HEIGHT;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return avatarScale * DEFAULT_AVATAR_EYE_HEIGHT;
|
return DEFAULT_AVATAR_EYE_HEIGHT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -255,12 +255,16 @@ public:
|
||||||
bool isFading() const { return _isFading; }
|
bool isFading() const { return _isFading; }
|
||||||
void updateFadingStatus(render::ScenePointer scene);
|
void updateFadingStatus(render::ScenePointer scene);
|
||||||
|
|
||||||
/**jsdoc
|
Q_INVOKABLE virtual float getEyeHeight() const override;
|
||||||
* Provides read only access to the current eye height of the avatar.
|
|
||||||
* @function Avatar.getEyeHeight
|
// returns eye height of avatar in meters, ignoreing avatar scale.
|
||||||
* @returns {number} eye height of avatar in meters
|
// if _targetScale is 1 then this will be identical to getEyeHeight;
|
||||||
*/
|
virtual float getUnscaledEyeHeight() const override;
|
||||||
Q_INVOKABLE float getEyeHeight() const;
|
|
||||||
|
// 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 float getModelScale() const { return _modelScale; }
|
||||||
virtual void setModelScale(float scale) { _modelScale = scale; }
|
virtual void setModelScale(float scale) { _modelScale = scale; }
|
||||||
|
@ -279,6 +283,7 @@ public slots:
|
||||||
void setModelURLFinished(bool success);
|
void setModelURLFinished(bool success);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
float getUnscaledEyeHeightFromSkeleton() const;
|
||||||
virtual const QString& getSessionDisplayNameForTransport() const override { return _empty; } // Save a tiny bit of bandwidth. Mixer won't look at what we send.
|
virtual const QString& getSessionDisplayNameForTransport() const override { return _empty; } // Save a tiny bit of bandwidth. Mixer won't look at what we send.
|
||||||
QString _empty{};
|
QString _empty{};
|
||||||
virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) override { _sessionDisplayName = sessionDisplayName; } // don't use no-op setter!
|
virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) override { _sessionDisplayName = sessionDisplayName; } // don't use no-op setter!
|
||||||
|
@ -349,7 +354,7 @@ protected:
|
||||||
RateCounter<> _skeletonModelSimulationRate;
|
RateCounter<> _skeletonModelSimulationRate;
|
||||||
RateCounter<> _jointDataSimulationRate;
|
RateCounter<> _jointDataSimulationRate;
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
class AvatarEntityDataHash {
|
class AvatarEntityDataHash {
|
||||||
public:
|
public:
|
||||||
AvatarEntityDataHash(uint32_t h) : hash(h) {};
|
AvatarEntityDataHash(uint32_t h) : hash(h) {};
|
||||||
|
|
|
@ -117,6 +117,55 @@ void AvatarData::setTargetScale(float targetScale) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float AvatarData::getDomainLimitedScale() const {
|
||||||
|
if (canMeasureEyeHeight()) {
|
||||||
|
const float minScale = getDomainMinScale();
|
||||||
|
const float maxScale = getDomainMaxScale();
|
||||||
|
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::getDomainMinScale() const {
|
||||||
|
float unscaledHeight = getUnscaledHeight();
|
||||||
|
const float EPSILON = 1.0e-4f;
|
||||||
|
if (unscaledHeight <= EPSILON) {
|
||||||
|
unscaledHeight = DEFAULT_AVATAR_HEIGHT;
|
||||||
|
}
|
||||||
|
return _domainMinimumHeight / unscaledHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
float AvatarData::getDomainMaxScale() const {
|
||||||
|
float unscaledHeight = getUnscaledHeight();
|
||||||
|
const float EPSILON = 1.0e-4f;
|
||||||
|
if (unscaledHeight <= EPSILON) {
|
||||||
|
unscaledHeight = DEFAULT_AVATAR_HEIGHT;
|
||||||
|
}
|
||||||
|
return _domainMaximumHeight / unscaledHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
float AvatarData::getUnscaledHeight() const {
|
||||||
|
const float eyeHeight = getUnscaledEyeHeight();
|
||||||
|
const float ratio = eyeHeight / DEFAULT_AVATAR_HEIGHT;
|
||||||
|
return eyeHeight + ratio * DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
glm::vec3 AvatarData::getHandPosition() const {
|
||||||
return getWorldOrientation() * _handPosition + getWorldPosition();
|
return getWorldOrientation() * _handPosition + getWorldPosition();
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include <QtScript/QScriptValueIterator>
|
#include <QtScript/QScriptValueIterator>
|
||||||
#include <QReadWriteLock>
|
#include <QReadWriteLock>
|
||||||
|
|
||||||
|
#include <AvatarConstants.h>
|
||||||
#include <JointData.h>
|
#include <JointData.h>
|
||||||
#include <NLPacket.h>
|
#include <NLPacket.h>
|
||||||
#include <Node.h>
|
#include <Node.h>
|
||||||
|
@ -257,9 +258,6 @@ namespace AvatarDataPacket {
|
||||||
size_t maxJointDataSize(size_t numJoints);
|
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 float MAX_AUDIO_LOUDNESS = 1000.0f; // close enough for mouth animation
|
||||||
|
|
||||||
const int AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS = 1000;
|
const int AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS = 1000;
|
||||||
|
@ -484,12 +482,38 @@ public:
|
||||||
// Scale
|
// Scale
|
||||||
virtual void setTargetScale(float targetScale);
|
virtual void setTargetScale(float targetScale);
|
||||||
|
|
||||||
float getDomainLimitedScale() const { return glm::clamp(_targetScale, _domainMinimumScale, _domainMaximumScale); }
|
float getDomainLimitedScale() const;
|
||||||
|
float getDomainMinScale() const;
|
||||||
|
float getDomainMaxScale() const;
|
||||||
|
|
||||||
void setDomainMinimumScale(float domainMinimumScale)
|
// returns eye height of avatar in meters, ignoreing avatar scale.
|
||||||
{ _domainMinimumScale = glm::clamp(domainMinimumScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); _scaleChanged = usecTimestampNow(); }
|
// if _targetScale is 1 then this will be identical to getEyeHeight;
|
||||||
void setDomainMaximumScale(float domainMaximumScale)
|
virtual float getUnscaledEyeHeight() const { return DEFAULT_AVATAR_EYE_HEIGHT; }
|
||||||
{ _domainMaximumScale = glm::clamp(domainMaximumScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); _scaleChanged = usecTimestampNow(); }
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
float getUnscaledHeight() const;
|
||||||
|
|
||||||
|
void setDomainMinimumHeight(float domainMinimumHeight);
|
||||||
|
void setDomainMaximumHeight(float domainMaximumHeight);
|
||||||
|
|
||||||
// Hand State
|
// Hand State
|
||||||
Q_INVOKABLE void setHandState(char s) { _handState = s; }
|
Q_INVOKABLE void setHandState(char s) { _handState = s; }
|
||||||
|
@ -698,8 +722,8 @@ protected:
|
||||||
|
|
||||||
// Body scale
|
// Body scale
|
||||||
float _targetScale;
|
float _targetScale;
|
||||||
float _domainMinimumScale { MIN_AVATAR_SCALE };
|
float _domainMinimumHeight { MIN_AVATAR_HEIGHT };
|
||||||
float _domainMaximumScale { MAX_AVATAR_SCALE };
|
float _domainMaximumHeight { MAX_AVATAR_HEIGHT };
|
||||||
|
|
||||||
// Hand state (are we grabbing something or not)
|
// Hand state (are we grabbing something or not)
|
||||||
char _handState;
|
char _handState;
|
||||||
|
|
|
@ -74,6 +74,8 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
||||||
return static_cast<PacketVersion>(AudioVersion::HighDynamicRangeVolume);
|
return static_cast<PacketVersion>(AudioVersion::HighDynamicRangeVolume);
|
||||||
case PacketType::ICEPing:
|
case PacketType::ICEPing:
|
||||||
return static_cast<PacketVersion>(IcePingVersion::SendICEPeerID);
|
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:
|
default:
|
||||||
return 17;
|
return 17;
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,7 +163,7 @@ void Model::setScale(const glm::vec3& scale) {
|
||||||
_scaledToFit = false;
|
_scaledToFit = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float SCALE_CHANGE_EPSILON = 0.01f;
|
const float SCALE_CHANGE_EPSILON = 0.001f;
|
||||||
|
|
||||||
void Model::setScaleInternal(const glm::vec3& scale) {
|
void Model::setScaleInternal(const glm::vec3& scale) {
|
||||||
if (glm::distance(_scale, scale) > SCALE_CHANGE_EPSILON) {
|
if (glm::distance(_scale, scale) > SCALE_CHANGE_EPSILON) {
|
||||||
|
|
|
@ -204,6 +204,9 @@ public:
|
||||||
/// Returns the extents of the model's mesh
|
/// Returns the extents of the model's mesh
|
||||||
Extents getMeshExtents() const;
|
Extents getMeshExtents() const;
|
||||||
|
|
||||||
|
/// Returns the unscaled extents of the model's mesh
|
||||||
|
Extents getUnscaledMeshExtents() const;
|
||||||
|
|
||||||
void setTranslation(const glm::vec3& translation);
|
void setTranslation(const glm::vec3& translation);
|
||||||
void setRotation(const glm::quat& rotation);
|
void setRotation(const glm::quat& rotation);
|
||||||
void setTransformNoUpdateRenderItems(const Transform& transform); // temporary HACK
|
void setTransformNoUpdateRenderItems(const Transform& transform); // temporary HACK
|
||||||
|
@ -276,9 +279,6 @@ protected:
|
||||||
void setBlendshapeCoefficients(const QVector<float>& coefficients) { _blendshapeCoefficients = coefficients; }
|
void setBlendshapeCoefficients(const QVector<float>& coefficients) { _blendshapeCoefficients = coefficients; }
|
||||||
const QVector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
|
const QVector<float>& getBlendshapeCoefficients() const { return _blendshapeCoefficients; }
|
||||||
|
|
||||||
/// Returns the unscaled extents of the model's mesh
|
|
||||||
Extents getUnscaledMeshExtents() const;
|
|
||||||
|
|
||||||
/// Clear the joint states
|
/// Clear the joint states
|
||||||
void clearJointState(int index);
|
void clearJointState(int index);
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
#ifndef hifi_AvatarConstants_h
|
#ifndef hifi_AvatarConstants_h
|
||||||
#define hifi_AvatarConstants_h
|
#define hifi_AvatarConstants_h
|
||||||
|
|
||||||
|
#include "GLMHelpers.h"
|
||||||
|
|
||||||
// 50th Percentile Man
|
// 50th Percentile Man
|
||||||
const float DEFAULT_AVATAR_HEIGHT = 1.755f; // meters
|
const float DEFAULT_AVATAR_HEIGHT = 1.755f; // meters
|
||||||
const float DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD = 0.11f; // 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_FALL_HEIGHT = 20.0f; // meters
|
||||||
const float DEFAULT_AVATAR_MIN_HOVER_HEIGHT = 2.5f; // 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
|
#endif // hifi_AvatarConstants_h
|
||||||
|
|
Loading…
Reference in a new issue