From 542af47e9edd7e5416643de01893a4d75138b1a6 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 29 Nov 2017 17:20:54 -0800 Subject: [PATCH] Fix for hand controller avatar scaling. * Added getDomainMaxScale() and getDomainMinScale() to JS api. * Updated scaleAvatar controller module to use this to prevent scaling past the limits. * Made sure that getDomainMaxScale() getDomainMinScale() and getUnscaledEyeHeight are thread safe, so that they can be invoked on the script thread. * Added signals to Model class that can be used to let observers know when the Rig has finished initializing it's skeleton. and also when the skeleton is no longer valid. These hooks are used to cache the unscaled eye height of the avatar. --- interface/src/avatar/MyAvatar.cpp | 3 +- .../src/avatars-renderer/Avatar.cpp | 30 +++++++++++++------ .../src/avatars-renderer/Avatar.h | 13 ++++++-- .../src/avatars-renderer/OtherAvatar.cpp | 2 ++ libraries/avatars/src/AvatarData.h | 18 +++++++++-- libraries/render-utils/src/Model.cpp | 2 ++ libraries/render-utils/src/Model.h | 2 ++ .../controllerModules/scaleAvatar.js | 6 +++- 8 files changed, 61 insertions(+), 15 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 1623fa3213..c874205611 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -114,7 +114,8 @@ MyAvatar::MyAvatar(QThread* thread) : _skeletonModel = std::make_shared(this, nullptr); connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); - + connect(_skeletonModel.get(), &Model::rigReady, this, &Avatar::rigReady); + connect(_skeletonModel.get(), &Model::rigReset, this, &Avatar::rigReset); using namespace recording; _skeletonModel->flagAsCauterized(); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 437ac623e3..bb7f141cd9 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1217,6 +1217,15 @@ void Avatar::setModelURLFinished(bool success) { } } +// rig is ready +void Avatar::rigReady() { + buildUnscaledEyeHeightCache(); +} + +// rig has been reset. +void Avatar::rigReset() { + clearUnscaledEyeHeightCache(); +} // create new model, can return an instance of a SoftAttachmentModel rather then Model static std::shared_ptr allocateAttachmentModel(bool isSoft, const Rig& rigOverride, bool isCauterized) { @@ -1584,18 +1593,17 @@ void Avatar::ensureInScene(AvatarSharedPointer self, const render::ScenePointer& } } +// thread-safe float Avatar::getEyeHeight() const { - - if (QThread::currentThread() != thread()) { - float result = DEFAULT_AVATAR_EYE_HEIGHT; - BLOCKING_INVOKE_METHOD(const_cast(this), "getEyeHeight", Q_RETURN_ARG(float, result)); - return result; - } - return getModelScale() * getUnscaledEyeHeight(); } +// thread-safe float Avatar::getUnscaledEyeHeight() const { + return _unscaledEyeHeightCache.get(); +} + +void Avatar::buildUnscaledEyeHeightCache() { float skeletonHeight = getUnscaledEyeHeightFromSkeleton(); // Sanity check by looking at the model extents. @@ -1606,12 +1614,16 @@ float Avatar::getUnscaledEyeHeight() const { // 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; + _unscaledEyeHeightCache.set(meshHeight); } else { - return skeletonHeight; + _unscaledEyeHeightCache.set(skeletonHeight); } } +void Avatar::clearUnscaledEyeHeightCache() { + _unscaledEyeHeightCache.set(DEFAULT_AVATAR_EYE_HEIGHT); +} + float Avatar::getUnscaledEyeHeightFromSkeleton() const { // TODO: if performance becomes a concern we can cache this value rather then computing it everytime. diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 44d4ef4c51..c75b54fdc4 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -257,8 +257,8 @@ public: 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; + // returns eye height of avatar in meters, ignoring 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, @@ -280,10 +280,17 @@ public slots: glm::vec3 getRightPalmPosition() const; glm::quat getRightPalmRotation() const; + // hooked up to Model::setURLFinished signal void setModelURLFinished(bool success); + // hooked up to Model::rigReady & rigReset signals + void rigReady(); + void rigReset(); + protected: float getUnscaledEyeHeightFromSkeleton() const; + void buildUnscaledEyeHeightCache(); + void clearUnscaledEyeHeightCache(); 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! @@ -384,6 +391,8 @@ protected: float _displayNameTargetAlpha { 1.0f }; float _displayNameAlpha { 1.0f }; + + ThreadSafeValueCache _unscaledEyeHeightCache { DEFAULT_AVATAR_EYE_HEIGHT }; }; #endif // hifi_Avatar_h diff --git a/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.cpp index e870e2de12..4382216575 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/OtherAvatar.cpp @@ -13,4 +13,6 @@ OtherAvatar::OtherAvatar(QThread* thread) : Avatar(thread) { _headData = new Head(this); _skeletonModel = std::make_shared(this, nullptr); connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); + connect(_skeletonModel.get(), &Model::rigReady, this, &Avatar::rigReady); + connect(_skeletonModel.get(), &Model::rigReset, this, &Avatar::rigReset); } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index de98675733..d7dd2837cb 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -483,8 +483,22 @@ public: virtual void setTargetScale(float targetScale); float getDomainLimitedScale() const; - float getDomainMinScale() const; - float getDomainMaxScale() const; + + /**jsdoc + * returns the minimum scale allowed for this avatar in the current domain. + * This value can change as the user changes avatars or when changing domains. + * @function AvatarData.getDomainMinScale + * @returns {number} minimum scale allowed for this avatar in the current domain. + */ + Q_INVOKABLE float getDomainMinScale() const; + + /**jsdoc + * returns the maximum scale allowed for this avatar in the current domain. + * This value can change as the user changes avatars or when changing domains. + * @function AvatarData.getDomainMaxScale + * @returns {number} maximum scale allowed for this avatar in the current domain. + */ + Q_INVOKABLE float getDomainMaxScale() const; // returns eye height of avatar in meters, ignoreing avatar scale. // if _targetScale is 1 then this will be identical to getEyeHeight; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 199cb29f53..c4bc435691 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -286,6 +286,7 @@ void Model::reset() { if (isLoaded()) { const FBXGeometry& geometry = getFBXGeometry(); _rig.reset(geometry); + emit rigReset(); } } @@ -322,6 +323,7 @@ bool Model::updateGeometry() { _blendedVertexBuffers.push_back(buffer); } needFullUpdate = true; + emit rigReady(); } return needFullUpdate; } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index fadd44b745..7568a17342 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -273,6 +273,8 @@ signals: void setURLFinished(bool success); void setCollisionModelURLFinished(bool success); void requestRenderUpdate(); + void rigReady(); + void rigReset(); protected: diff --git a/scripts/system/controllers/controllerModules/scaleAvatar.js b/scripts/system/controllers/controllerModules/scaleAvatar.js index e4ae3654e1..1868b0228a 100644 --- a/scripts/system/controllers/controllerModules/scaleAvatar.js +++ b/scripts/system/controllers/controllerModules/scaleAvatar.js @@ -12,6 +12,10 @@ (function () { var dispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js"); + function clamp(val, min, max) { + return Math.max(min, Math.min(max, val)); + } + function ScaleAvatar(hand) { this.hand = hand; this.scalingStartAvatarScale = 0; @@ -61,7 +65,7 @@ controllerData.controllerLocations[this.otherHand()].position)); var newAvatarScale = (scalingCurrentDistance / this.scalingStartDistance) * this.scalingStartAvatarScale; - MyAvatar.scale = newAvatarScale; + MyAvatar.scale = clamp(newAvatarScale, MyAvatar.getDomainMinScale(), MyAvatar.getDomainMaxScale()); MyAvatar.scaleChanged(); } return dispatcherUtils.makeRunningValues(true, [], []);