mirror of
https://github.com/Armored-Dragon/overte.git
synced 2025-03-11 16:13:16 +01:00
use Rig instead of Avatar for height calculations
This commit is contained in:
parent
bcd00f98d0
commit
d985d1bff0
5 changed files with 99 additions and 120 deletions
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue