mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 11:48:09 +02:00
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.
This commit is contained in:
parent
ed231fa4a1
commit
542af47e9e
8 changed files with 61 additions and 15 deletions
|
@ -114,7 +114,8 @@ MyAvatar::MyAvatar(QThread* thread) :
|
||||||
|
|
||||||
_skeletonModel = std::make_shared<MySkeletonModel>(this, nullptr);
|
_skeletonModel = std::make_shared<MySkeletonModel>(this, nullptr);
|
||||||
connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished);
|
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;
|
using namespace recording;
|
||||||
_skeletonModel->flagAsCauterized();
|
_skeletonModel->flagAsCauterized();
|
||||||
|
|
|
@ -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
|
// create new model, can return an instance of a SoftAttachmentModel rather then Model
|
||||||
static std::shared_ptr<Model> allocateAttachmentModel(bool isSoft, const Rig& rigOverride, bool isCauterized) {
|
static std::shared_ptr<Model> 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 {
|
float Avatar::getEyeHeight() const {
|
||||||
|
|
||||||
if (QThread::currentThread() != thread()) {
|
|
||||||
float result = DEFAULT_AVATAR_EYE_HEIGHT;
|
|
||||||
BLOCKING_INVOKE_METHOD(const_cast<Avatar*>(this), "getEyeHeight", Q_RETURN_ARG(float, result));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return getModelScale() * getUnscaledEyeHeight();
|
return getModelScale() * getUnscaledEyeHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// thread-safe
|
||||||
float Avatar::getUnscaledEyeHeight() const {
|
float Avatar::getUnscaledEyeHeight() const {
|
||||||
|
return _unscaledEyeHeightCache.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Avatar::buildUnscaledEyeHeightCache() {
|
||||||
float skeletonHeight = getUnscaledEyeHeightFromSkeleton();
|
float skeletonHeight = getUnscaledEyeHeightFromSkeleton();
|
||||||
|
|
||||||
// Sanity check by looking at the model extents.
|
// 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.
|
// This helps prevent absurdly large avatars from exceeding the domain height limit.
|
||||||
const float MESH_SLOP_RATIO = 1.5f;
|
const float MESH_SLOP_RATIO = 1.5f;
|
||||||
if (meshHeight > skeletonHeight * MESH_SLOP_RATIO) {
|
if (meshHeight > skeletonHeight * MESH_SLOP_RATIO) {
|
||||||
return meshHeight;
|
_unscaledEyeHeightCache.set(meshHeight);
|
||||||
} else {
|
} else {
|
||||||
return skeletonHeight;
|
_unscaledEyeHeightCache.set(skeletonHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Avatar::clearUnscaledEyeHeightCache() {
|
||||||
|
_unscaledEyeHeightCache.set(DEFAULT_AVATAR_EYE_HEIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
float Avatar::getUnscaledEyeHeightFromSkeleton() const {
|
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.
|
||||||
|
|
|
@ -257,8 +257,8 @@ public:
|
||||||
|
|
||||||
Q_INVOKABLE virtual float getEyeHeight() const override;
|
Q_INVOKABLE virtual float getEyeHeight() const override;
|
||||||
|
|
||||||
// returns eye height of avatar in meters, ignoreing avatar scale.
|
// returns eye height of avatar in meters, ignoring avatar scale.
|
||||||
// if _targetScale is 1 then this will be identical to getEyeHeight;
|
// if _targetScale is 1 then this will be identical to getEyeHeight.
|
||||||
virtual float getUnscaledEyeHeight() const override;
|
virtual float getUnscaledEyeHeight() const override;
|
||||||
|
|
||||||
// returns true, if an acurate eye height estimage can be obtained by inspecting the avatar model skeleton and geometry,
|
// 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::vec3 getRightPalmPosition() const;
|
||||||
glm::quat getRightPalmRotation() const;
|
glm::quat getRightPalmRotation() const;
|
||||||
|
|
||||||
|
// hooked up to Model::setURLFinished signal
|
||||||
void setModelURLFinished(bool success);
|
void setModelURLFinished(bool success);
|
||||||
|
|
||||||
|
// hooked up to Model::rigReady & rigReset signals
|
||||||
|
void rigReady();
|
||||||
|
void rigReset();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
float getUnscaledEyeHeightFromSkeleton() const;
|
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.
|
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!
|
||||||
|
@ -384,6 +391,8 @@ protected:
|
||||||
|
|
||||||
float _displayNameTargetAlpha { 1.0f };
|
float _displayNameTargetAlpha { 1.0f };
|
||||||
float _displayNameAlpha { 1.0f };
|
float _displayNameAlpha { 1.0f };
|
||||||
|
|
||||||
|
ThreadSafeValueCache<float> _unscaledEyeHeightCache { DEFAULT_AVATAR_EYE_HEIGHT };
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_Avatar_h
|
#endif // hifi_Avatar_h
|
||||||
|
|
|
@ -13,4 +13,6 @@ OtherAvatar::OtherAvatar(QThread* thread) : Avatar(thread) {
|
||||||
_headData = new Head(this);
|
_headData = new Head(this);
|
||||||
_skeletonModel = std::make_shared<SkeletonModel>(this, nullptr);
|
_skeletonModel = std::make_shared<SkeletonModel>(this, nullptr);
|
||||||
connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished);
|
connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished);
|
||||||
|
connect(_skeletonModel.get(), &Model::rigReady, this, &Avatar::rigReady);
|
||||||
|
connect(_skeletonModel.get(), &Model::rigReset, this, &Avatar::rigReset);
|
||||||
}
|
}
|
||||||
|
|
|
@ -483,8 +483,22 @@ public:
|
||||||
virtual void setTargetScale(float targetScale);
|
virtual void setTargetScale(float targetScale);
|
||||||
|
|
||||||
float getDomainLimitedScale() const;
|
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.
|
// returns eye height of avatar in meters, ignoreing avatar scale.
|
||||||
// if _targetScale is 1 then this will be identical to getEyeHeight;
|
// if _targetScale is 1 then this will be identical to getEyeHeight;
|
||||||
|
|
|
@ -286,6 +286,7 @@ void Model::reset() {
|
||||||
if (isLoaded()) {
|
if (isLoaded()) {
|
||||||
const FBXGeometry& geometry = getFBXGeometry();
|
const FBXGeometry& geometry = getFBXGeometry();
|
||||||
_rig.reset(geometry);
|
_rig.reset(geometry);
|
||||||
|
emit rigReset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,6 +323,7 @@ bool Model::updateGeometry() {
|
||||||
_blendedVertexBuffers.push_back(buffer);
|
_blendedVertexBuffers.push_back(buffer);
|
||||||
}
|
}
|
||||||
needFullUpdate = true;
|
needFullUpdate = true;
|
||||||
|
emit rigReady();
|
||||||
}
|
}
|
||||||
return needFullUpdate;
|
return needFullUpdate;
|
||||||
}
|
}
|
||||||
|
|
|
@ -273,6 +273,8 @@ signals:
|
||||||
void setURLFinished(bool success);
|
void setURLFinished(bool success);
|
||||||
void setCollisionModelURLFinished(bool success);
|
void setCollisionModelURLFinished(bool success);
|
||||||
void requestRenderUpdate();
|
void requestRenderUpdate();
|
||||||
|
void rigReady();
|
||||||
|
void rigReset();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,10 @@
|
||||||
(function () {
|
(function () {
|
||||||
var dispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js");
|
var dispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js");
|
||||||
|
|
||||||
|
function clamp(val, min, max) {
|
||||||
|
return Math.max(min, Math.min(max, val));
|
||||||
|
}
|
||||||
|
|
||||||
function ScaleAvatar(hand) {
|
function ScaleAvatar(hand) {
|
||||||
this.hand = hand;
|
this.hand = hand;
|
||||||
this.scalingStartAvatarScale = 0;
|
this.scalingStartAvatarScale = 0;
|
||||||
|
@ -61,7 +65,7 @@
|
||||||
controllerData.controllerLocations[this.otherHand()].position));
|
controllerData.controllerLocations[this.otherHand()].position));
|
||||||
|
|
||||||
var newAvatarScale = (scalingCurrentDistance / this.scalingStartDistance) * this.scalingStartAvatarScale;
|
var newAvatarScale = (scalingCurrentDistance / this.scalingStartDistance) * this.scalingStartAvatarScale;
|
||||||
MyAvatar.scale = newAvatarScale;
|
MyAvatar.scale = clamp(newAvatarScale, MyAvatar.getDomainMinScale(), MyAvatar.getDomainMaxScale());
|
||||||
MyAvatar.scaleChanged();
|
MyAvatar.scaleChanged();
|
||||||
}
|
}
|
||||||
return dispatcherUtils.makeRunningValues(true, [], []);
|
return dispatcherUtils.makeRunningValues(true, [], []);
|
||||||
|
|
Loading…
Reference in a new issue