use Rig instead of Avatar for height calculations

This commit is contained in:
Thijs Wenker 2019-03-04 19:59:29 +01:00
parent bcd00f98d0
commit d985d1bff0
5 changed files with 99 additions and 120 deletions

View file

@ -12,8 +12,9 @@
#include "AvatarDoctor.h"
#include <model-networking/ModelCache.h>
#include <AvatarConstants.h>
#include <Rig.h>
#include <ResourceManager.h>
#include <QDir>
#include <FSTReader.h>
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<SkeletonModel>(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();

View file

@ -16,7 +16,7 @@
#include <QUrl>
#include <QVector>
#include <QVariantMap>
#include <avatars-renderer/Avatar.h>
#include "GeometryCache.h"
struct AvatarDiagnosticResult {
QString message;
@ -25,21 +25,6 @@ struct AvatarDiagnosticResult {
Q_DECLARE_METATYPE(AvatarDiagnosticResult)
Q_DECLARE_METATYPE(QVector<AvatarDiagnosticResult>)
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;

View file

@ -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;
}
}

View file

@ -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();

View file

@ -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;
}