mirror of
https://github.com/overte-org/overte.git
synced 2025-04-19 12:23:24 +02:00
Avatar Doctor
This commit is contained in:
parent
8faff57033
commit
3026fd625a
11 changed files with 468 additions and 8 deletions
|
@ -0,0 +1,125 @@
|
|||
import QtQuick 2.0
|
||||
|
||||
import "../../controlsUit" 1.0 as HifiControls
|
||||
import "../../stylesUit" 1.0
|
||||
|
||||
Item {
|
||||
id: diagnosingScreen
|
||||
|
||||
visible: false
|
||||
|
||||
property var avatarDoctor: null
|
||||
property var errors: []
|
||||
|
||||
signal doneDiagnosing
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!diagnosingScreen.visible) {
|
||||
//if (debugDelay.running) {
|
||||
// debugDelay.stop();
|
||||
//}
|
||||
return;
|
||||
}
|
||||
//debugDelay.start();
|
||||
avatarDoctor = AvatarPackagerCore.currentAvatarProject.diagnose();
|
||||
avatarDoctor.complete.connect(function(errors) {
|
||||
console.warn("avatarDoctor.complete " + JSON.stringify(errors));
|
||||
diagnosingScreen.errors = errors;
|
||||
AvatarPackagerCore.currentAvatarProject.hasErrors = errors.length > 0;
|
||||
AvatarPackagerCore.addCurrentProjectToRecentProjects();
|
||||
|
||||
// FIXME: can't seem to change state here so do it with a timer instead
|
||||
doneTimer.start();
|
||||
});
|
||||
avatarDoctor.startDiagnosing();
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: doneTimer
|
||||
interval: 1
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: {
|
||||
doneDiagnosing();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Timer {
|
||||
id: debugDelay
|
||||
interval: 5000
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: {
|
||||
if (Math.random() > 0.5) {
|
||||
// ERROR
|
||||
avatarPackager.state = AvatarPackagerState.avatarDoctorErrorReport;
|
||||
} else {
|
||||
// SUCCESS
|
||||
avatarPackager.state = AvatarPackagerState.project;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
property var footer: Item {
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: 17
|
||||
HifiControls.Button {
|
||||
id: cancelButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
height: 30
|
||||
width: 133
|
||||
text: qsTr("Cancel")
|
||||
onClicked: {
|
||||
avatarPackager.state = AvatarPackagerState.main;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LoadingCircle {
|
||||
id: loadingCircle
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: 46
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
width: 163
|
||||
height: 163
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
id: testingPackageTitle
|
||||
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
top: loadingCircle.bottom
|
||||
topMargin: 5
|
||||
}
|
||||
|
||||
text: "Testing package for errors"
|
||||
size: 28
|
||||
color: "white"
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
id: testingPackageText
|
||||
|
||||
anchors {
|
||||
top: testingPackageTitle.bottom
|
||||
topMargin: 26
|
||||
left: parent.left
|
||||
leftMargin: 21
|
||||
right: parent.right
|
||||
rightMargin: 16
|
||||
}
|
||||
|
||||
text: "We are trying to find errors in your project so you can quickly understand and resolve them."
|
||||
size: 21
|
||||
color: "white"
|
||||
lineHeight: 33
|
||||
lineHeightMode: Text.FixedHeight
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
import QtQuick 2.0
|
||||
|
||||
import "../../controlsUit" 1.0 as HifiControls
|
||||
import "../../stylesUit" 1.0
|
||||
|
||||
Item {
|
||||
id: errorReport
|
||||
|
||||
visible: false
|
||||
|
||||
property alias errors: errorRepeater.model
|
||||
|
||||
property var footer: Item {
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: 17
|
||||
HifiControls.Button {
|
||||
id: tryAgainButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: continueButton.left
|
||||
anchors.rightMargin: 22
|
||||
height: 40
|
||||
width: 134
|
||||
text: qsTr("Try Again")
|
||||
// colorScheme: root.colorScheme
|
||||
onClicked: {
|
||||
avatarPackager.state = AvatarPackagerState.avatarDoctorDiagnose;
|
||||
}
|
||||
}
|
||||
|
||||
HifiControls.Button {
|
||||
id: continueButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
height: 40
|
||||
width: 133
|
||||
text: qsTr("Continue")
|
||||
color: hifi.buttons.blue
|
||||
colorScheme: root.colorScheme
|
||||
onClicked: {
|
||||
avatarPackager.state = AvatarPackagerState.project;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
id: errorReportIcon
|
||||
text: hifi.glyphs.alert
|
||||
size: 315
|
||||
color: "#EA4C5F"
|
||||
anchors {
|
||||
top: parent.top
|
||||
//topMargin: 73
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
top: errorReportIcon.bottom
|
||||
topMargin: 27
|
||||
leftMargin: 13
|
||||
rightMargin: 13
|
||||
}
|
||||
spacing: 7
|
||||
|
||||
Repeater {
|
||||
id: errorRepeater
|
||||
/*model: [
|
||||
{message: "Rig is not Hifi compatible", url: "http://www.highfidelity.com/"},
|
||||
{message: "Bone limit exceeds 256", url: "http://www.highfidelity.com/2"},
|
||||
{message: "Unsupported Texture", url: "http://www.highfidelity.com/texture"},
|
||||
{message: "Rig is not Hifi compatible", url: "http://www.highfidelity.com/"},
|
||||
{message: "Bone limit exceeds 256", url: "http://www.highfidelity.com/2"},
|
||||
{message: "Unsupported Texture", url: "http://www.highfidelity.com/texture"}
|
||||
]*/
|
||||
|
||||
Item {
|
||||
height: 37
|
||||
width: parent.width
|
||||
|
||||
HiFiGlyphs {
|
||||
id: errorIcon
|
||||
text: hifi.glyphs.alert
|
||||
size: 56
|
||||
color: "#EA4C5F"
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
}
|
||||
}
|
||||
|
||||
RalewayRegular {
|
||||
id: errorLink
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: errorIcon.right
|
||||
right: parent.right
|
||||
}
|
||||
linkColor: "#00B4EF"// style.colors.blueHighlight
|
||||
size: 28
|
||||
text: "<a href='javascript:void'>" + modelData.message + "</a>"
|
||||
onLinkActivated: Qt.openUrlExternally(modelData.url)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -143,6 +143,18 @@ Item {
|
|||
PropertyChanges { target: createAvatarProject; visible: true }
|
||||
PropertyChanges { target: avatarPackagerFooter; content: createAvatarProject.footer }
|
||||
},
|
||||
State {
|
||||
name: AvatarPackagerState.avatarDoctorDiagnose
|
||||
PropertyChanges { target: avatarPackagerHeader; title: AvatarPackagerCore.currentAvatarProject.name }
|
||||
PropertyChanges { target: avatarDoctorDiagnose; visible: true }
|
||||
PropertyChanges { target: avatarPackagerFooter; content: avatarDoctorDiagnose.footer }
|
||||
},
|
||||
State {
|
||||
name: AvatarPackagerState.avatarDoctorErrorReport
|
||||
PropertyChanges { target: avatarPackagerHeader; title: AvatarPackagerCore.currentAvatarProject.name }
|
||||
PropertyChanges { target: avatarDoctorErrorReport; visible: true }
|
||||
PropertyChanges { target: avatarPackagerFooter; content: avatarDoctorErrorReport.footer }
|
||||
},
|
||||
State {
|
||||
name: AvatarPackagerState.project
|
||||
PropertyChanges { target: avatarPackagerHeader; title: AvatarPackagerCore.currentAvatarProject.name; canRename: true }
|
||||
|
@ -168,7 +180,7 @@ Item {
|
|||
return status;
|
||||
}
|
||||
avatarProject.reset();
|
||||
avatarPackager.state = AvatarPackagerState.project;
|
||||
avatarPackager.state = AvatarPackagerState.avatarDoctorDiagnose;
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -242,6 +254,23 @@ Item {
|
|||
color: "#404040"
|
||||
}
|
||||
|
||||
AvatarDoctorDiagnose {
|
||||
id: avatarDoctorDiagnose
|
||||
anchors.fill: parent
|
||||
onErrorsChanged: {
|
||||
avatarDoctorErrorReport.errors = avatarDoctorDiagnose.errors;
|
||||
}
|
||||
onDoneDiagnosing: {
|
||||
avatarPackager.state = avatarDoctorDiagnose.errors.length > 0 ? AvatarPackagerState.avatarDoctorErrorReport
|
||||
: AvatarPackagerState.project;
|
||||
}
|
||||
}
|
||||
|
||||
AvatarDoctorErrorReport {
|
||||
id: avatarDoctorErrorReport
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
AvatarProject {
|
||||
id: avatarProject
|
||||
colorScheme: root.colorScheme
|
||||
|
@ -383,6 +412,7 @@ Item {
|
|||
title: modelData.name
|
||||
path: modelData.projectPath
|
||||
onOpen: avatarPackager.openProject(modelData.path)
|
||||
hasError: modelData.hadErrors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,4 +7,6 @@ Item {
|
|||
readonly property string project: "project"
|
||||
readonly property string createProject: "createProject"
|
||||
readonly property string projectUpload: "projectUpload"
|
||||
readonly property string avatarDoctorDiagnose: "avatarDoctorDiagnose"
|
||||
readonly property string avatarDoctorErrorReport: "avatarDoctorErrorReport"
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ Item {
|
|||
|
||||
property alias title: title.text
|
||||
property alias path: path.text
|
||||
property alias hasError: errorIcon.visible
|
||||
|
||||
property color textColor: "#E3E3E3"
|
||||
property color hoverTextColor: "#121212"
|
||||
|
@ -54,7 +55,7 @@ Item {
|
|||
|
||||
RalewayBold {
|
||||
id: title
|
||||
elide: "ElideRight"
|
||||
elide: Text.ElideRight
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: 13
|
||||
|
@ -76,12 +77,24 @@ Item {
|
|||
right: background.right
|
||||
rightMargin: 16
|
||||
}
|
||||
elide: "ElideLeft"
|
||||
elide: Text.ElideLeft
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: "<path missing>"
|
||||
size: 20
|
||||
}
|
||||
|
||||
HiFiGlyphs {
|
||||
id: errorIcon
|
||||
visible: false
|
||||
text: hifi.glyphs.alert
|
||||
size: 56
|
||||
color: "#EA4C5F"
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
|
|
101
interface/src/avatar/AvatarDoctor.cpp
Normal file
101
interface/src/avatar/AvatarDoctor.cpp
Normal file
|
@ -0,0 +1,101 @@
|
|||
//
|
||||
// AvatarDoctor.cpp
|
||||
//
|
||||
//
|
||||
// Created by Thijs Wenker on 2/12/2019.
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AvatarDoctor.h"
|
||||
#include <model-networking/ModelCache.h>
|
||||
|
||||
AvatarDoctor::AvatarDoctor(QUrl avatarFSTFileUrl) :
|
||||
_avatarFSTFileUrl(std::move(avatarFSTFileUrl)) {
|
||||
}
|
||||
|
||||
void AvatarDoctor::startDiagnosing() {
|
||||
_errors.clear();
|
||||
const auto resource = DependencyManager::get<ModelCache>()->getGeometryResource(_avatarFSTFileUrl);
|
||||
const auto resourceLoaded = [this, resource](bool success) {
|
||||
// MODEL
|
||||
if (!success) {
|
||||
_errors.push_back({ "Model file cannot be opened", QUrl("http://www.highfidelity.com/docs") });
|
||||
emit complete(getErrors());
|
||||
return;
|
||||
}
|
||||
const auto avatarModel = resource.data()->getHFMModel();
|
||||
if (!avatarModel.originalURL.endsWith(".fbx")) {
|
||||
_errors.push_back({ "Unsupported avatar model format", QUrl("http://www.highfidelity.com/docs") });
|
||||
emit complete(getErrors());
|
||||
return;
|
||||
}
|
||||
|
||||
// RIG
|
||||
if (avatarModel.joints.isEmpty()) {
|
||||
_errors.push_back({ "Avatar has no rig", QUrl("http://www.highfidelity.com/docs") });
|
||||
}
|
||||
else {
|
||||
if (avatarModel.joints.length() > 256) {
|
||||
_errors.push_back({ "Avatar has over 256 bones", QUrl("http://www.highfidelity.com/docs") });
|
||||
}
|
||||
// Avatar does not have Hips bone mapped
|
||||
if (!avatarModel.getJointNames().contains("Hips")) {
|
||||
_errors.push_back({ "Hips are not mapped", QUrl("http://www.highfidelity.com/docs") });
|
||||
}
|
||||
if (!avatarModel.getJointNames().contains("Spine")) {
|
||||
_errors.push_back({ "Spine is not mapped", QUrl("http://www.highfidelity.com/docs") });
|
||||
}
|
||||
if (!avatarModel.getJointNames().contains("Head")) {
|
||||
_errors.push_back({ "Head is not mapped", QUrl("http://www.highfidelity.com/docs") });
|
||||
}
|
||||
}
|
||||
|
||||
// SCALE
|
||||
const float DEFAULT_HEIGHT = 1.75f;
|
||||
const float RECOMMENDED_MIN_HEIGHT = DEFAULT_HEIGHT * 0.25;
|
||||
const float RECOMMENDED_MAX_HEIGHT = DEFAULT_HEIGHT * 1.5;
|
||||
|
||||
float avatarHeight = avatarModel.getMeshExtents().largestDimension();
|
||||
|
||||
qWarning() << "avatarHeight" << avatarHeight;
|
||||
if (avatarHeight < RECOMMENDED_MIN_HEIGHT) {
|
||||
_errors.push_back({ "Avatar is possibly smaller then expected.", QUrl("http://www.highfidelity.com/docs") });
|
||||
}
|
||||
else if (avatarHeight > RECOMMENDED_MAX_HEIGHT) {
|
||||
_errors.push_back({ "Avatar is possibly larger then expected.", QUrl("http://www.highfidelity.com/docs") });
|
||||
}
|
||||
|
||||
// BLENDSHAPES
|
||||
|
||||
// TEXTURES
|
||||
//avatarModel.materials.
|
||||
|
||||
|
||||
emit complete(getErrors());
|
||||
};
|
||||
|
||||
if (resource) {
|
||||
if (resource->isLoaded()) {
|
||||
resourceLoaded(!resource->isFailed());
|
||||
} else {
|
||||
connect(resource.data(), &GeometryResource::finished, this, resourceLoaded);
|
||||
}
|
||||
} else {
|
||||
_errors.push_back({ "Model file cannot be opened", QUrl("http://www.highfidelity.com/docs") });
|
||||
emit complete(getErrors());
|
||||
}
|
||||
}
|
||||
|
||||
QVariantList AvatarDoctor::getErrors() const {
|
||||
QVariantList result;
|
||||
for (const auto& error : _errors) {
|
||||
QVariantMap errorVariant;
|
||||
errorVariant.insert("message", error.message);
|
||||
errorVariant.insert("url", error.url);
|
||||
result.append(errorVariant);
|
||||
}
|
||||
return result;
|
||||
}
|
50
interface/src/avatar/AvatarDoctor.h
Normal file
50
interface/src/avatar/AvatarDoctor.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// AvatarDoctor.h
|
||||
//
|
||||
//
|
||||
// Created by Thijs Wenker on 02/12/2019.
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef hifi_AvatarDoctor_h
|
||||
#define hifi_AvatarDoctor_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QUrl>
|
||||
#include <QVector>
|
||||
#include <QVariantMap>
|
||||
|
||||
struct AvatarDiagnosticResult {
|
||||
|
||||
//public:
|
||||
// AvatarDiagnosticResult() {}
|
||||
// AvatarDiagnosticResult(QString message, QUrl url) : _message(std::move(message)), _url(std::move(url)) { }
|
||||
//private:
|
||||
QString message;
|
||||
QUrl url;
|
||||
};
|
||||
Q_DECLARE_METATYPE(AvatarDiagnosticResult)
|
||||
Q_DECLARE_METATYPE(QVector<AvatarDiagnosticResult>)
|
||||
|
||||
class AvatarDoctor : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AvatarDoctor(QUrl avatarFSTFileUrl);
|
||||
|
||||
Q_INVOKABLE void startDiagnosing();
|
||||
|
||||
Q_INVOKABLE QVariantList getErrors() const;
|
||||
|
||||
signals:
|
||||
void complete(QVariantList errors);
|
||||
|
||||
private:
|
||||
QUrl _avatarFSTFileUrl;
|
||||
QVector<AvatarDiagnosticResult> _errors;
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarDoctor_h
|
|
@ -31,6 +31,9 @@ AvatarPackager::AvatarPackager() {
|
|||
qmlRegisterType<MarketplaceItemUploader>();
|
||||
qRegisterMetaType<AvatarPackager*>();
|
||||
qRegisterMetaType<AvatarProject*>();
|
||||
qRegisterMetaType<AvatarDoctor*>();
|
||||
qRegisterMetaType<AvatarDiagnosticResult>();
|
||||
qRegisterMetaType<QVector<AvatarDiagnosticResult>>();
|
||||
qRegisterMetaType<AvatarProjectStatus::AvatarProjectStatus>();
|
||||
qmlRegisterUncreatableMetaObject(
|
||||
AvatarProjectStatus::staticMetaObject,
|
||||
|
@ -84,7 +87,7 @@ void AvatarPackager::addCurrentProjectToRecentProjects() {
|
|||
_recentProjects.removeOne(removeProject);
|
||||
}
|
||||
|
||||
const auto newRecentProject = RecentAvatarProject(_currentAvatarProject->getProjectName(), fstPath);
|
||||
const auto newRecentProject = RecentAvatarProject(_currentAvatarProject->getProjectName(), fstPath, _currentAvatarProject->getHasErrors());
|
||||
_recentProjects.prepend(newRecentProject);
|
||||
|
||||
while (_recentProjects.size() > MAX_RECENT_PROJECTS) {
|
||||
|
@ -101,6 +104,7 @@ QVariantList AvatarPackager::recentProjectsToVariantList(bool includeProjectPath
|
|||
QVariantMap projectVariant;
|
||||
projectVariant.insert("name", project.getProjectName());
|
||||
projectVariant.insert("path", project.getProjectFSTPath());
|
||||
projectVariant.insert("hadErrors", project.getHadErrors());
|
||||
if (includeProjectPaths) {
|
||||
projectVariant.insert("projectPath", project.getProjectPath());
|
||||
}
|
||||
|
@ -113,7 +117,10 @@ void AvatarPackager::recentProjectsFromVariantList(QVariantList projectsVariant)
|
|||
_recentProjects.clear();
|
||||
for (const auto& projectVariant : projectsVariant) {
|
||||
auto map = projectVariant.toMap();
|
||||
_recentProjects.append(RecentAvatarProject(map.value("name").toString(), map.value("path").toString()));
|
||||
_recentProjects.append(RecentAvatarProject(
|
||||
map.value("name").toString(),
|
||||
map.value("path").toString(),
|
||||
map.value("hadErrors", false).toBool()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,19 +26,23 @@ public:
|
|||
RecentAvatarProject() = default;
|
||||
|
||||
|
||||
RecentAvatarProject(QString projectName, QString projectFSTPath) {
|
||||
RecentAvatarProject(QString projectName, QString projectFSTPath, bool hadErrors) {
|
||||
_projectName = projectName;
|
||||
_projectFSTPath = projectFSTPath;
|
||||
_hadErrors = hadErrors;
|
||||
}
|
||||
RecentAvatarProject(const RecentAvatarProject& other) {
|
||||
_projectName = other._projectName;
|
||||
_projectFSTPath = other._projectFSTPath;
|
||||
_hadErrors = other._hadErrors;
|
||||
}
|
||||
|
||||
QString getProjectName() const { return _projectName; }
|
||||
|
||||
QString getProjectFSTPath() const { return _projectFSTPath; }
|
||||
|
||||
bool getHadErrors() const { return _hadErrors; }
|
||||
|
||||
QString getProjectPath() const {
|
||||
return QFileInfo(_projectFSTPath).absoluteDir().absolutePath();
|
||||
}
|
||||
|
@ -50,6 +54,7 @@ public:
|
|||
private:
|
||||
QString _projectName;
|
||||
QString _projectFSTPath;
|
||||
bool _hadErrors;
|
||||
|
||||
};
|
||||
|
||||
|
@ -73,6 +78,8 @@ public:
|
|||
return AvatarProject::isValidNewProjectName(projectPath, projectName);
|
||||
}
|
||||
|
||||
Q_INVOKABLE void addCurrentProjectToRecentProjects();
|
||||
|
||||
signals:
|
||||
void avatarProjectChanged();
|
||||
void recentProjectsChanged();
|
||||
|
@ -84,8 +91,6 @@ private:
|
|||
|
||||
void setAvatarProject(AvatarProject* avatarProject);
|
||||
|
||||
void addCurrentProjectToRecentProjects();
|
||||
|
||||
AvatarProject* _currentAvatarProject { nullptr };
|
||||
QVector<RecentAvatarProject> _recentProjects;
|
||||
|
||||
|
|
|
@ -243,6 +243,12 @@ MarketplaceItemUploader* AvatarProject::upload(bool updateExisting) {
|
|||
return uploader;
|
||||
}
|
||||
|
||||
AvatarDoctor* AvatarProject::diagnose() {
|
||||
auto avatarDoctor = new AvatarDoctor(QUrl(getFSTPath()));
|
||||
|
||||
return avatarDoctor;
|
||||
}
|
||||
|
||||
void AvatarProject::openInInventory() const {
|
||||
constexpr int TIME_TO_WAIT_FOR_INVENTORY_TO_OPEN_MS { 1000 };
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#define hifi_AvatarProject_h
|
||||
|
||||
#include "MarketplaceItemUploader.h"
|
||||
#include "AvatarDoctor.h"
|
||||
#include "ProjectFile.h"
|
||||
#include "FST.h"
|
||||
|
||||
|
@ -53,11 +54,14 @@ class AvatarProject : public QObject {
|
|||
Q_PROPERTY(QString projectFSTPath READ getFSTPath CONSTANT)
|
||||
Q_PROPERTY(QString projectFBXPath READ getFBXPath CONSTANT)
|
||||
Q_PROPERTY(QString name READ getProjectName WRITE setProjectName NOTIFY nameChanged)
|
||||
Q_PROPERTY(bool hasErrors READ getHasErrors WRITE setHasErrors NOTIFY hasErrorsChanged)
|
||||
|
||||
public:
|
||||
Q_INVOKABLE MarketplaceItemUploader* upload(bool updateExisting);
|
||||
Q_INVOKABLE void openInInventory() const;
|
||||
Q_INVOKABLE QStringList getProjectFiles() const;
|
||||
Q_INVOKABLE AvatarDoctor* diagnose();
|
||||
|
||||
|
||||
Q_INVOKABLE QString getProjectName() const { return _fst->getName(); }
|
||||
Q_INVOKABLE void setProjectName(const QString& newProjectName) {
|
||||
|
@ -72,6 +76,8 @@ public:
|
|||
Q_INVOKABLE QString getFBXPath() const {
|
||||
return QDir::cleanPath(QDir(_projectPath).absoluteFilePath(_fst->getModelPath()));
|
||||
}
|
||||
Q_INVOKABLE bool getHasErrors() const { return _hasErrors; }
|
||||
Q_INVOKABLE void setHasErrors(bool hasErrors) { _hasErrors = hasErrors; }
|
||||
|
||||
/**
|
||||
* returns the AvatarProject or a nullptr on failure.
|
||||
|
@ -92,6 +98,7 @@ public:
|
|||
signals:
|
||||
void nameChanged();
|
||||
void projectFilesChanged();
|
||||
void hasErrorsChanged();
|
||||
|
||||
private:
|
||||
AvatarProject(const QString& fstPath, const QByteArray& data);
|
||||
|
@ -110,6 +117,8 @@ private:
|
|||
QDir _directory;
|
||||
QList<ProjectFilePath> _projectFiles{};
|
||||
QString _projectPath;
|
||||
|
||||
bool _hasErrors { false };
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarProject_h
|
||||
|
|
Loading…
Reference in a new issue