mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-08 14:42:09 +02:00
Merge pull request #15206 from thoys/feat/avatarTools/avatarDoctorWarningURLs
Case 21353: Avatar Doctor documentation / Video URLs
This commit is contained in:
commit
16d791703d
4 changed files with 63 additions and 35 deletions
|
@ -133,7 +133,7 @@ Item {
|
|||
states: [
|
||||
State {
|
||||
name: AvatarPackagerState.main
|
||||
PropertyChanges { target: avatarPackagerHeader; title: qsTr("Avatar Packager"); docsEnabled: true; backButtonVisible: false }
|
||||
PropertyChanges { target: avatarPackagerHeader; title: qsTr("Avatar Packager"); docsEnabled: true; videoEnabled: true; backButtonVisible: false }
|
||||
PropertyChanges { target: avatarPackagerMain; visible: true }
|
||||
PropertyChanges { target: avatarPackagerFooter; content: avatarPackagerMain.footer }
|
||||
},
|
||||
|
@ -229,7 +229,11 @@ Item {
|
|||
}
|
||||
|
||||
function openDocs() {
|
||||
Qt.openUrlExternally("https://docs.highfidelity.com/create/avatars/create-avatars#how-to-package-your-avatar");
|
||||
Qt.openUrlExternally("https://docs.highfidelity.com/create/avatars/package-avatar.html");
|
||||
}
|
||||
|
||||
function openVideo() {
|
||||
Qt.openUrlExternally("https://youtu.be/zrkEowu_yps");
|
||||
}
|
||||
|
||||
AvatarPackagerHeader {
|
||||
|
@ -243,6 +247,9 @@ Item {
|
|||
onDocsButtonClicked: {
|
||||
avatarPackager.openDocs();
|
||||
}
|
||||
onVideoButtonClicked: {
|
||||
avatarPackager.openVideo();
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
|
|
|
@ -13,6 +13,7 @@ ShadowRectangle {
|
|||
|
||||
property string title: qsTr("Avatar Packager")
|
||||
property alias docsEnabled: docs.visible
|
||||
property alias videoEnabled: video.visible
|
||||
property bool backButtonVisible: true // If false, is not visible and does not take up space
|
||||
property bool backButtonEnabled: true // If false, is not visible but does not affect space
|
||||
property bool canRename: false
|
||||
|
@ -24,6 +25,7 @@ ShadowRectangle {
|
|||
|
||||
signal backButtonClicked
|
||||
signal docsButtonClicked
|
||||
signal videoButtonClicked
|
||||
|
||||
RalewayButton {
|
||||
id: back
|
||||
|
@ -126,6 +128,20 @@ ShadowRectangle {
|
|||
}
|
||||
}
|
||||
|
||||
RalewayButton {
|
||||
id: video
|
||||
visible: false
|
||||
size: 28
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: docs.left
|
||||
anchors.rightMargin: 16
|
||||
|
||||
text: qsTr("Video")
|
||||
|
||||
onClicked: videoButtonClicked()
|
||||
}
|
||||
|
||||
RalewayButton {
|
||||
id: docs
|
||||
visible: false
|
||||
|
@ -137,8 +153,6 @@ ShadowRectangle {
|
|||
|
||||
text: qsTr("Docs")
|
||||
|
||||
onClicked: {
|
||||
docsButtonClicked();
|
||||
}
|
||||
onClicked: docsButtonClicked()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ static QStringList HAND_MAPPING_SUFFIXES = {
|
|||
"HandThumb1",
|
||||
};
|
||||
|
||||
const QUrl DEFAULT_DOCS_URL = QUrl("https://docs.highfidelity.com/create/avatars/create-avatars.html#create-your-own-avatar");
|
||||
const QUrl PACKAGE_AVATAR_DOCS_BASE_URL = QUrl("https://docs.highfidelity.com/create/avatars/package-avatar.html");
|
||||
|
||||
AvatarDoctor::AvatarDoctor(const QUrl& avatarFSTFileUrl) :
|
||||
_avatarFSTFileUrl(avatarFSTFileUrl) {
|
||||
|
@ -85,7 +85,7 @@ void AvatarDoctor::startDiagnosing() {
|
|||
const auto resourceLoaded = [this, resource](bool success) {
|
||||
// MODEL
|
||||
if (!success) {
|
||||
_errors.push_back({ "Model file cannot be opened.", DEFAULT_DOCS_URL });
|
||||
addError("Model file cannot be opened.", "missing-file");
|
||||
emit complete(getErrors());
|
||||
return;
|
||||
}
|
||||
|
@ -93,45 +93,45 @@ void AvatarDoctor::startDiagnosing() {
|
|||
const auto model = resource.data();
|
||||
const auto avatarModel = resource.data()->getHFMModel();
|
||||
if (!avatarModel.originalURL.endsWith(".fbx")) {
|
||||
_errors.push_back({ "Unsupported avatar model format.", DEFAULT_DOCS_URL });
|
||||
addError("Unsupported avatar model format.", "unsupported-format");
|
||||
emit complete(getErrors());
|
||||
return;
|
||||
}
|
||||
|
||||
// RIG
|
||||
if (avatarModel.joints.isEmpty()) {
|
||||
_errors.push_back({ "Avatar has no rig.", DEFAULT_DOCS_URL });
|
||||
addError("Avatar has no rig.", "no-rig");
|
||||
} else {
|
||||
auto jointNames = avatarModel.getJointNames();
|
||||
|
||||
if (avatarModel.joints.length() > NETWORKED_JOINTS_LIMIT) {
|
||||
_errors.push_back({tr( "Avatar has over %n bones.", "", NETWORKED_JOINTS_LIMIT), DEFAULT_DOCS_URL });
|
||||
addError(tr( "Avatar has over %n bones.", "", NETWORKED_JOINTS_LIMIT), "maximum-bone-limit");
|
||||
}
|
||||
// Avatar does not have Hips bone mapped
|
||||
if (!jointNames.contains("Hips")) {
|
||||
_errors.push_back({ "Hips are not mapped.", DEFAULT_DOCS_URL });
|
||||
addError("Hips are not mapped.", "hips-not-mapped");
|
||||
}
|
||||
if (!jointNames.contains("Spine")) {
|
||||
_errors.push_back({ "Spine is not mapped.", DEFAULT_DOCS_URL });
|
||||
addError("Spine is not mapped.", "spine-not-mapped");
|
||||
}
|
||||
if (!jointNames.contains("Spine1")) {
|
||||
_errors.push_back({ "Chest (Spine1) is not mapped.", DEFAULT_DOCS_URL });
|
||||
addError("Chest (Spine1) is not mapped.", "chest-not-mapped");
|
||||
}
|
||||
if (!jointNames.contains("Neck")) {
|
||||
_errors.push_back({ "Neck is not mapped.", DEFAULT_DOCS_URL });
|
||||
addError("Neck is not mapped.", "neck-not-mapped");
|
||||
}
|
||||
if (!jointNames.contains("Head")) {
|
||||
_errors.push_back({ "Head is not mapped.", DEFAULT_DOCS_URL });
|
||||
addError("Head is not mapped.", "head-not-mapped");
|
||||
}
|
||||
|
||||
if (!jointNames.contains("LeftEye")) {
|
||||
if (jointNames.contains("RightEye")) {
|
||||
_errors.push_back({ "LeftEye is not mapped.", DEFAULT_DOCS_URL });
|
||||
addError("LeftEye is not mapped.", "eye-not-mapped");
|
||||
} else {
|
||||
_errors.push_back({ "Eyes are not mapped.", DEFAULT_DOCS_URL });
|
||||
addError("Eyes are not mapped.", "eye-not-mapped");
|
||||
}
|
||||
} else if (!jointNames.contains("RightEye")) {
|
||||
_errors.push_back({ "RightEye is not mapped.", DEFAULT_DOCS_URL });
|
||||
addError("RightEye is not mapped.", "eye-not-mapped");
|
||||
}
|
||||
|
||||
const auto checkJointAsymmetry = [jointNames] (const QStringList& jointMappingSuffixes) {
|
||||
|
@ -159,13 +159,13 @@ void AvatarDoctor::startDiagnosing() {
|
|||
};
|
||||
|
||||
if (checkJointAsymmetry(ARM_MAPPING_SUFFIXES)) {
|
||||
_errors.push_back({ "Asymmetrical arm bones.", DEFAULT_DOCS_URL });
|
||||
addError("Asymmetrical arm bones.", "asymmetrical-bones");
|
||||
}
|
||||
if (checkJointAsymmetry(HAND_MAPPING_SUFFIXES)) {
|
||||
_errors.push_back({ "Asymmetrical hand bones.", DEFAULT_DOCS_URL });
|
||||
addError("Asymmetrical hand bones.", "asymmetrical-bones");
|
||||
}
|
||||
if (checkJointAsymmetry(LEG_MAPPING_SUFFIXES)) {
|
||||
_errors.push_back({ "Asymmetrical leg bones.", DEFAULT_DOCS_URL });
|
||||
addError("Asymmetrical leg bones.", "asymmetrical-bones");
|
||||
}
|
||||
|
||||
// Multiple skeleton root joints checkup
|
||||
|
@ -177,7 +177,7 @@ void AvatarDoctor::startDiagnosing() {
|
|||
}
|
||||
|
||||
if (skeletonRootJoints > 1) {
|
||||
_errors.push_back({ "Multiple top-level joints found.", DEFAULT_DOCS_URL });
|
||||
addError("Multiple top-level joints found.", "multiple-top-level-joints");
|
||||
}
|
||||
|
||||
Rig rig;
|
||||
|
@ -191,9 +191,9 @@ void AvatarDoctor::startDiagnosing() {
|
|||
const float RECOMMENDED_MAX_HEIGHT = DEFAULT_AVATAR_HEIGHT * 1.5f;
|
||||
|
||||
if (avatarHeight < RECOMMENDED_MIN_HEIGHT) {
|
||||
_errors.push_back({ "Avatar is possibly too short.", DEFAULT_DOCS_URL });
|
||||
addError("Avatar is possibly too short.", "short-avatar");
|
||||
} else if (avatarHeight > RECOMMENDED_MAX_HEIGHT) {
|
||||
_errors.push_back({ "Avatar is possibly too tall.", DEFAULT_DOCS_URL });
|
||||
addError("Avatar is possibly too tall.", "tall-avatar");
|
||||
}
|
||||
|
||||
// HipsNotOnGround
|
||||
|
@ -204,7 +204,7 @@ void AvatarDoctor::startDiagnosing() {
|
|||
const auto hipJoint = avatarModel.joints.at(avatarModel.getJointIndex("Hips"));
|
||||
|
||||
if (hipsPosition.y < HIPS_GROUND_MIN_Y) {
|
||||
_errors.push_back({ "Hips are on ground.", DEFAULT_DOCS_URL });
|
||||
addError("Hips are on ground.", "hips-on-ground");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -223,7 +223,7 @@ void AvatarDoctor::startDiagnosing() {
|
|||
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_DOCS_URL });
|
||||
addError("Hips/Spine/Chest overlap.", "overlap-error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -240,21 +240,21 @@ void AvatarDoctor::startDiagnosing() {
|
|||
const auto& uniqueJointValues = jointValues.toSet();
|
||||
for (const auto& jointName: uniqueJointValues) {
|
||||
if (jointValues.count(jointName) > 1) {
|
||||
_errors.push_back({ tr("%1 is mapped multiple times.").arg(jointName), DEFAULT_DOCS_URL });
|
||||
addError(tr("%1 is mapped multiple times.").arg(jointName), "mapped-multiple-times");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isDescendantOfJointWhenJointsExist("Spine", "Hips")) {
|
||||
_errors.push_back({ "Spine is not a child of Hips.", DEFAULT_DOCS_URL });
|
||||
addError("Spine is not a child of Hips.", "spine-not-child");
|
||||
}
|
||||
|
||||
if (!isDescendantOfJointWhenJointsExist("Spine1", "Spine")) {
|
||||
_errors.push_back({ "Spine1 is not a child of Spine.", DEFAULT_DOCS_URL });
|
||||
addError("Spine1 is not a child of Spine.", "spine1-not-child");
|
||||
}
|
||||
|
||||
if (!isDescendantOfJointWhenJointsExist("Head", "Spine1")) {
|
||||
_errors.push_back({ "Head is not a child of Spine1.", DEFAULT_DOCS_URL });
|
||||
addError("Head is not a child of Spine1.", "head-not-child");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -300,7 +300,7 @@ void AvatarDoctor::startDiagnosing() {
|
|||
connect(resource.data(), &GeometryResource::finished, this, resourceLoaded);
|
||||
}
|
||||
} else {
|
||||
_errors.push_back({ "Model file cannot be opened", DEFAULT_DOCS_URL });
|
||||
addError("Model file cannot be opened", "missing-file");
|
||||
emit complete(getErrors());
|
||||
}
|
||||
}
|
||||
|
@ -345,7 +345,7 @@ void AvatarDoctor::diagnoseTextures() {
|
|||
QUrl(avatarModel.originalURL)).resolved(QUrl("textures"));
|
||||
|
||||
if (texturesFound == 0) {
|
||||
_errors.push_back({ tr("No textures assigned."), DEFAULT_DOCS_URL });
|
||||
addError(tr("No textures assigned."), "no-textures-assigned");
|
||||
}
|
||||
|
||||
if (!externalTextures.empty()) {
|
||||
|
@ -356,11 +356,10 @@ void AvatarDoctor::diagnoseTextures() {
|
|||
auto checkTextureLoadingComplete = [this]() mutable {
|
||||
if (_checkedTextureCount == _externalTextureCount) {
|
||||
if (_missingTextureCount > 0) {
|
||||
_errors.push_back({ tr("Missing %n texture(s).","", _missingTextureCount), DEFAULT_DOCS_URL });
|
||||
addError(tr("Missing %n texture(s).","", _missingTextureCount), "missing-textures");
|
||||
}
|
||||
if (_unsupportedTextureCount > 0) {
|
||||
_errors.push_back({ tr("%n unsupported texture(s) found.", "", _unsupportedTextureCount),
|
||||
DEFAULT_DOCS_URL });
|
||||
addError(tr("%n unsupported texture(s) found.", "", _unsupportedTextureCount), "unsupported-textures");
|
||||
}
|
||||
|
||||
emit complete(getErrors());
|
||||
|
@ -411,6 +410,12 @@ void AvatarDoctor::diagnoseTextures() {
|
|||
}
|
||||
}
|
||||
|
||||
void AvatarDoctor::addError(const QString& errorMessage, const QString& docFragment) {
|
||||
QUrl documentationURL = PACKAGE_AVATAR_DOCS_BASE_URL;
|
||||
documentationURL.setFragment(docFragment);
|
||||
_errors.push_back({ errorMessage, documentationURL });
|
||||
}
|
||||
|
||||
QVariantList AvatarDoctor::getErrors() const {
|
||||
QVariantList result;
|
||||
for (const auto& error : _errors) {
|
||||
|
|
|
@ -40,6 +40,8 @@ signals:
|
|||
private:
|
||||
void diagnoseTextures();
|
||||
|
||||
void addError(const QString& errorMessage, const QString& docFragment);
|
||||
|
||||
QUrl _avatarFSTFileUrl;
|
||||
QVector<AvatarDiagnosticResult> _errors;
|
||||
|
||||
|
|
Loading…
Reference in a new issue