diff --git a/interface/src/avatar/AvatarDoctor.cpp b/interface/src/avatar/AvatarDoctor.cpp index 66433f005b..f15c8bbc6c 100644 --- a/interface/src/avatar/AvatarDoctor.cpp +++ b/interface/src/avatar/AvatarDoctor.cpp @@ -12,8 +12,9 @@ #include "AvatarDoctor.h" #include #include +#include #include - +#include #include const int NETWORKED_JOINTS_LIMIT = 256; @@ -56,18 +57,6 @@ static QStringList HAND_MAPPING_SUFFIXES = { const QUrl DEFAULT_URL = QUrl("https://docs.highfidelity.com/create/avatars/create-avatars.html#create-your-own-avatar"); -DiagnosableAvatar::DiagnosableAvatar(QThread* thread) : Avatar(thread) { - // give the pointer to our head to inherited _headData variable from AvatarData - _headData = new Head(this); - _skeletonModel = std::make_shared(this, nullptr); - _skeletonModel->setLoadingPriority(MYAVATAR_LOADING_PRIORITY); - connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); - connect(_skeletonModel.get(), &Model::rigReady, this, &Avatar::rigReady); - connect(_skeletonModel.get(), &Model::rigReset, this, &Avatar::rigReset); -} - -DiagnosableAvatar::~DiagnosableAvatar() = default; - AvatarDoctor::AvatarDoctor(const QUrl& avatarFSTFileUrl) : _avatarFSTFileUrl(avatarFSTFileUrl) { @@ -178,59 +167,61 @@ void AvatarDoctor::startDiagnosing() { if (checkJointAsymmetry(LEG_MAPPING_SUFFIXES)) { _errors.push_back({ "Asymmetrical leg bones.", DEFAULT_URL }); } - _avatar = new DiagnosableAvatar(QThread::currentThread()); - _avatar->setSkeletonModelURL(_avatarFSTFileUrl); - if (_avatar->getSkeletonModel()->updateGeometry()) { - // Rig has been fully loaded - // SCALE - const float RECOMMENDED_MIN_HEIGHT = DEFAULT_AVATAR_HEIGHT * 0.25f; - const float RECOMMENDED_MAX_HEIGHT = DEFAULT_AVATAR_HEIGHT * 1.5f; - const float avatarHeight = _avatar->getHeight(); - if (avatarHeight < RECOMMENDED_MIN_HEIGHT) { - _errors.push_back({ "Avatar is possibly too short.", DEFAULT_URL }); - } else if (avatarHeight > RECOMMENDED_MAX_HEIGHT) { - _errors.push_back({ "Avatar is possibly too tall.", DEFAULT_URL }); } - auto rig = &_avatar->getSkeletonModel()->getRig(); - // HipsNotOnGround - auto hipsIndex = rig->indexOfJoint("Hips"); - if (hipsIndex >= 0) { - glm::vec3 hipsPosition; - if (rig->getJointPosition(hipsIndex, hipsPosition)) { - const auto hipJoint = avatarModel.joints.at(avatarModel.getJointIndex("Hips")); + const auto rig = new Rig(); + rig->reset(avatarModel); + const float eyeHeight = rig->getUnscaledEyeHeight(); + const float ratio = eyeHeight / DEFAULT_AVATAR_HEIGHT; + const float avatarHeight = eyeHeight + ratio * DEFAULT_AVATAR_EYE_TO_TOP_OF_HEAD; + qDebug() << "avatarHeight = " << avatarHeight; - if (hipsPosition.y < HIPS_GROUND_MIN_Y) { - _errors.push_back({ "Hips are on ground.", DEFAULT_URL }); - } - } - } + // SCALE + const float RECOMMENDED_MIN_HEIGHT = DEFAULT_AVATAR_HEIGHT * 0.25f; + const float RECOMMENDED_MAX_HEIGHT = DEFAULT_AVATAR_HEIGHT * 1.5f; - // HipsSpineChestNotCoincident - auto spineIndex = rig->indexOfJoint("Spine"); - auto chestIndex = rig->indexOfJoint("Spine1"); - if (hipsIndex >= 0 && spineIndex >= 0 && chestIndex >= 0) { - glm::vec3 hipsPosition; - glm::vec3 spinePosition; - glm::vec3 chestPosition; - if (rig->getJointPosition(hipsIndex, hipsPosition) && - rig->getJointPosition(spineIndex, spinePosition) && - rig->getJointPosition(chestIndex, chestPosition)) { + if (avatarHeight < RECOMMENDED_MIN_HEIGHT) { + _errors.push_back({ "Avatar is possibly too short.", DEFAULT_URL }); + } else if (avatarHeight > RECOMMENDED_MAX_HEIGHT) { + _errors.push_back({ "Avatar is possibly too tall.", DEFAULT_URL }); + } - const auto hipsToSpine = glm::length(hipsPosition - spinePosition); - const auto spineToChest = glm::length(spinePosition - chestPosition); - if (hipsToSpine < HIPS_SPINE_CHEST_MIN_SEPARATION && spineToChest < HIPS_SPINE_CHEST_MIN_SEPARATION) { - _errors.push_back({ "Hips/Spine/Chest overlap.", DEFAULT_URL }); - } + // HipsNotOnGround + auto hipsIndex = rig->indexOfJoint("Hips"); + if (hipsIndex >= 0) { + glm::vec3 hipsPosition; + if (rig->getJointPosition(hipsIndex, hipsPosition)) { + const auto hipJoint = avatarModel.joints.at(avatarModel.getJointIndex("Hips")); + + if (hipsPosition.y < HIPS_GROUND_MIN_Y) { + _errors.push_back({ "Hips are on ground.", DEFAULT_URL }); } } } - _avatar->deleteLater(); - _avatar = nullptr; + + // HipsSpineChestNotCoincident + auto spineIndex = rig->indexOfJoint("Spine"); + auto chestIndex = rig->indexOfJoint("Spine1"); + if (hipsIndex >= 0 && spineIndex >= 0 && chestIndex >= 0) { + glm::vec3 hipsPosition; + glm::vec3 spinePosition; + glm::vec3 chestPosition; + if (rig->getJointPosition(hipsIndex, hipsPosition) && + rig->getJointPosition(spineIndex, spinePosition) && + rig->getJointPosition(chestIndex, chestPosition)) { + + const auto hipsToSpine = glm::length(hipsPosition - spinePosition); + const auto spineToChest = glm::length(spinePosition - chestPosition); + if (hipsToSpine < HIPS_SPINE_CHEST_MIN_SEPARATION && spineToChest < HIPS_SPINE_CHEST_MIN_SEPARATION) { + _errors.push_back({ "Hips/Spine/Chest overlap.", DEFAULT_URL }); + } + } + } + rig->deleteLater(); auto mapping = resource->getMapping(); diff --git a/interface/src/avatar/AvatarDoctor.h b/interface/src/avatar/AvatarDoctor.h index 17397d99df..780f600bed 100644 --- a/interface/src/avatar/AvatarDoctor.h +++ b/interface/src/avatar/AvatarDoctor.h @@ -16,7 +16,7 @@ #include #include #include -#include +#include "GeometryCache.h" struct AvatarDiagnosticResult { QString message; @@ -25,21 +25,6 @@ struct AvatarDiagnosticResult { Q_DECLARE_METATYPE(AvatarDiagnosticResult) Q_DECLARE_METATYPE(QVector) - -class DiagnosableAvatar: public Avatar { -public: - explicit DiagnosableAvatar(QThread* thread); - virtual ~DiagnosableAvatar(); - - void simulate(float deltaTime, bool inView) override { - - } - void rebuildCollisionShape() override { - - } - virtual void instantiableAvatar() override { }; -}; - class AvatarDoctor : public QObject { Q_OBJECT public: @@ -66,8 +51,6 @@ private: int _materialMappingCount = 0; int _materialMappingLoadedCount = 0; - DiagnosableAvatar* _avatar { nullptr }; - GeometryResource::Pointer _model; bool _isDiagnosing = false; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 0fe03c7074..07bdfde189 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -2179,3 +2179,52 @@ void Rig::initFlow(bool isActive) { _networkFlow.cleanUp(); } } + +float Rig::getUnscaledEyeHeight() const { + // Normally the model offset transform will contain the avatar scale factor, we explicitly remove it here. + AnimPose modelOffsetWithoutAvatarScale(glm::vec3(1.0f), getModelOffsetPose().rot(), getModelOffsetPose().trans()); + AnimPose geomToRigWithoutAvatarScale = modelOffsetWithoutAvatarScale * 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 = indexOfJoint("HeadTop_End"); + int headJoint = indexOfJoint("Head"); + int eyeJoint = indexOfJoint("LeftEye") != -1 ? indexOfJoint("LeftEye") : indexOfJoint("RightEye"); + int toeJoint = indexOfJoint("LeftToeBase") != -1 ? indexOfJoint("LeftToeBase") : 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 = getAnimSkeleton(); + if (eyeJoint >= 0 && toeJoint >= 0) { + // 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 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 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 = 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 headHeight = skeleton->getAbsoluteDefaultPose(headTopJoint).trans().y - GROUND_Y; + return scaleFactor * (headHeight - headHeight * ratio); + } else if (headJoint >= 0) { + // 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 = skeleton->getAbsoluteDefaultPose(headJoint).trans().y - GROUND_Y; + return scaleFactor * (neckHeight + neckHeight * ratio); + } else { + return DEFAULT_AVATAR_EYE_HEIGHT; + } +} diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 2f0e2ad65b..df13ff5c2b 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -26,6 +26,7 @@ #include "SimpleMovingAverage.h" #include "AnimUtil.h" #include "Flow.h" +#include "AvatarConstants.h" class Rig; class AnimInverseKinematics; @@ -237,6 +238,8 @@ public: void initFlow(bool isActive); Flow& getFlow() { return _internalFlow; } + float getUnscaledEyeHeight() const; + signals: void onLoadComplete(); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index d3ae030296..f3e671143b 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -2040,54 +2040,7 @@ float Avatar::getUnscaledEyeHeightFromSkeleton() const { // TODO: if performance becomes a concern we can cache this value rather then computing it everytime. 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 = skeleton->getAbsoluteDefaultPose(eyeJoint).trans().y - skeleton->getAbsoluteDefaultPose(toeJoint).trans().y; - return scaleFactor * eyeHeight; - } else if (eyeJoint >= 0) { - // 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 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 = 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 headHeight = skeleton->getAbsoluteDefaultPose(headTopJoint).trans().y - GROUND_Y; - return scaleFactor * (headHeight - headHeight * ratio); - } else if (headJoint >= 0) { - // 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 = skeleton->getAbsoluteDefaultPose(headJoint).trans().y - GROUND_Y; - return scaleFactor * (neckHeight + neckHeight * ratio); - } else { - return DEFAULT_AVATAR_EYE_HEIGHT; - } + return _skeletonModel->getRig().getUnscaledEyeHeight(); } else { return DEFAULT_AVATAR_EYE_HEIGHT; }