3
0
Fork 0
mirror of https://github.com/JulianGro/overte.git synced 2025-04-29 17:23:12 +02:00

- fst read/write should work

- images are being copied into the correct directory
- scripts are added to fst upon project load
- modal overlay fix
This commit is contained in:
Thijs Wenker 2018-12-21 19:34:54 +01:00
parent 1da5b3fae2
commit ad2d9bc79a
14 changed files with 177 additions and 60 deletions

View file

@ -7,7 +7,7 @@ import "../controlsUit" 1.0 as HifiControls
import "../stylesUit" 1.0
import "../windows" as Windows
import "../dialogs"
import "avatarPackager"
import "./avatarPackager" 1.0
import "avatarapp" 1.0 as AvatarApp
Windows.ScrollingWindow {
@ -29,32 +29,46 @@ Windows.ScrollingWindow {
height: pane.height
width: pane.width
Rectangle {
id: modalOverlay
anchors.fill: parent
z: 20
color: "#aa031b33"
visible: false
// This mouse area captures the cursor events while the modalOverlay is active
MouseArea {
anchors.fill: parent
propagateComposedEvents: false;
hoverEnabled: true;
}
}
AvatarApp.MessageBox {
id: popup
anchors.fill: parent
visible: false
}
// FIXME: modal overlay does not show
Column {
id: avatarPackager
anchors.fill: parent
state: "main"
states: [
State {
name: "main"
name: AvatarPackagerState.main
PropertyChanges { target: avatarPackagerHeader; title: qsTr("Avatar Packager"); faqEnabled: true; backButtonEnabled: false }
PropertyChanges { target: avatarPackagerMain; visible: true }
PropertyChanges { target: avatarPackagerFooter; content: avatarPackagerMain.footer }
},
State {
name: "createProject"
name: AvatarPackagerState.createProject
PropertyChanges { target: avatarPackagerHeader; title: qsTr("Create Project") }
PropertyChanges { target: createAvatarProject; visible: true }
PropertyChanges { target: avatarPackagerFooter; content: createAvatarProject.footer }
},
State {
name: "project"
name: AvatarPackagerState.project
PropertyChanges { target: avatarPackagerHeader; title: AvatarPackagerCore.currentAvatarProject.name }
PropertyChanges { target: avatarProject; visible: true }
PropertyChanges { target: avatarPackagerFooter; content: avatarProject.footer }
@ -67,6 +81,8 @@ Windows.ScrollingWindow {
}
]
property alias showModalOverlay: modalOverlay.visible
AvatarPackagerHeader {
id: avatarPackagerHeader
onBackButtonClicked: {
@ -119,7 +135,7 @@ Windows.ScrollingWindow {
text: qsTr("New Project")
colorScheme: root.colorScheme
onClicked: {
avatarPackager.state = "createProject";
avatarPackager.state = AvatarPackagerState.createProject;
}
}
@ -133,7 +149,7 @@ Windows.ScrollingWindow {
color: hifi.buttons.blue
colorScheme: root.colorScheme
onClicked: {
// TODO: make the dialog modal
avatarPackager.showModalOverlay = true;
let browser = desktop.fileDialog({
selectDirectory: false,
dir: fileDialogHelper.pathToUrl(AvatarPackagerCore.AVATAR_PROJECTS_PATH),
@ -142,14 +158,15 @@ Windows.ScrollingWindow {
});
browser.canceled.connect(function() {
avatarPackager.showModalOverlay = false;
});
browser.selectedFile.connect(function(fileUrl) {
let fstFilePath = fileDialogHelper.urlToPath(fileUrl);
let currentAvatarProject = AvatarPackagerCore.openAvatarProject(fstFilePath);
if (currentAvatarProject) {
avatarPackager.state = "project";
avatarPackager.state = AvatarPackagerState.project;
avatarPackager.showModalOverlay = false;
}
});
}

View file

@ -8,6 +8,7 @@ Rectangle {
color: "#404040"
height: content === defaultContent ? 0 : 74
visible: content !== defaultContent
width: parent.width
property var content: Item { id: defaultContent }

View file

@ -65,7 +65,7 @@ Rectangle {
anchors.right: parent.right
anchors.rightMargin: 16
anchors.verticalCenter: faq.verticalCenter
text: qsTr("FAQ")
text: qsTr("Docs")
color: "white"
}
}

View file

@ -0,0 +1,9 @@
pragma Singleton
import QtQuick 2.6
Item {
id: singleton
readonly property string main: "main"
readonly property string project: "project"
readonly property string createProject: "createProject"
}

View file

@ -27,7 +27,7 @@ Item {
Window.alert('Failed to create project')
return;
}
avatarPackager.state = "project";
avatarPackager.state = AvatarPackagerState.project;
}
}
}
@ -37,6 +37,26 @@ Item {
height: parent.height
width: parent.width
property var errorMessages: QtObject {
readonly property string fileExists: "A folder with that name already exists at that location. Please choose a different project name or location."
}
RalewayRegular {
id: errorMessage
visible: text !== ""
text: ""
color: "#EA4C5F";
wrapMode: Text.WordWrap
size: 20
anchors {
top: createAvatarColumns.bottom
bottom: parent.bottom
left: parent.left
right: parent.right
}
}
Column {
id: createAvatarColumns
anchors.left: parent.left
@ -45,6 +65,8 @@ Item {
spacing: 17
property string defaultFileBrowserPath: fileDialogHelper.pathToUrl(AvatarPackagerCore.AVATAR_PROJECTS_PATH)
ProjectInputControl {
id: name
label: "Name"
@ -57,9 +79,8 @@ Item {
colorScheme: root.colorScheme
browseEnabled: true
browseFolder: true
browseDir: fileDialogHelper.pathToUrl(AvatarPackagerCore.AVATAR_PROJECTS_PATH)
browseDir: text !== "" ? fileDialogHelper.pathToUrl(text) : createAvatarColumns.defaultFileBrowserPath
browseTitle: "Project Location"
text: fileDialogHelper.pathToUrl(AvatarPackagerCore.AVATAR_PROJECTS_PATH)
onTextChanged: {
//TODO: valid folder? Does project with name exist here already?
}
@ -71,11 +92,13 @@ Item {
colorScheme: root.colorScheme
browseEnabled: true
browseFolder: false
browseDir: fileDialogHelper.pathToUrl(AvatarPackagerCore.AVATAR_PROJECTS_PATH)
browseDir: text !== "" ? fileDialogHelper.pathToUrl(text) : createAvatarColumns.defaultFileBrowserPath
browseFilter: "Avatar Model File (*.fbx)"
browseTitle: "Open Avatar Model (.fbx)"
onTextChanged: {
//TODO: try to get texture folder from fbx if none is set?
if (avatarModel.text !== "") {
textureFolder.browseDir = fileDialogHelper.pathToUrl(avatarModel.text.split('/')[0]);
}
}
}
@ -85,7 +108,7 @@ Item {
colorScheme: root.colorScheme
browseEnabled: true
browseFolder: true
browseDir: fileDialogHelper.pathToUrl(AvatarPackagerCore.AVATAR_PROJECTS_PATH)
browseDir: text !== "" ? fileDialogHelper.pathToUrl(text) : createAvatarColumns.defaultFileBrowserPath
browseTitle: "Texture Folder"
onTextChanged: {
//TODO: valid folder?
@ -93,16 +116,5 @@ Item {
}
}
}
RalewayRegular {
text: "A folder with that name already exists at that location. Please choose a different project name or location."
color: "#EA4C5F";
wrapMode: Text.WordWrap
size: 20
anchors {
top: createAvatarColumns.bottom
bottom: parent.bottom
left: parent.left
right: parent.right
}
}
}

View file

@ -55,7 +55,7 @@ Column {
text: qsTr("Browse")
colorScheme: root.colorScheme
onClicked: {
// TODO: make the dialog modal
avatarPackager.showModalOverlay = true;
let browser = desktop.fileDialog({
selectDirectory: browseFolder,
dir: browseDir,
@ -64,11 +64,12 @@ Column {
});
browser.canceled.connect(function() {
avatarPackager.showModalOverlay = false;
});
browser.selectedFile.connect(function(fileUrl) {
input.text = fileDialogHelper.urlToPath(fileUrl);
avatarPackager.showModalOverlay = false;
});
}
}

View file

@ -0,0 +1,2 @@
module AvatarPackager
singleton AvatarPackagerState 1.0 AvatarPackagerState.qml

View file

@ -30,6 +30,9 @@ AvatarPackager::AvatarPackager() {
qRegisterMetaType<AvatarPackager*>();
qRegisterMetaType<AvatarProject*>();
});
QDir defaultProjectsDir(AvatarProject::getDefaultProjectsPath());
defaultProjectsDir.mkpath(".");
}
bool AvatarPackager::open() {
@ -57,7 +60,7 @@ AvatarProject* AvatarPackager::createAvatarProject(const QString& projectsFolder
if (_currentAvatarProject) {
_currentAvatarProject->deleteLater();
}
_currentAvatarProject = AvatarProject::createAvatarProject(avatarProjectName, avatarModelPath);
_currentAvatarProject = AvatarProject::createAvatarProject(projectsFolder, avatarProjectName, avatarModelPath, textureFolder);
qDebug() << "_currentAvatarProject has" << (QQmlEngine::objectOwnership(_currentAvatarProject) == QQmlEngine::CppOwnership ? "CPP" : "JS") << "OWNERSHIP";
QQmlEngine::setObjectOwnership(_currentAvatarProject, QQmlEngine::CppOwnership);
emit avatarProjectChanged();

View file

@ -21,6 +21,7 @@
#include "FBXSerializer.h"
#include <ui/TabletScriptingInterface.h>
#include <graphics/TextureMap.h>
#include "scripting/HMDScriptingInterface.h"
AvatarProject* AvatarProject::openAvatarProject(const QString& path) {
@ -36,17 +37,25 @@ AvatarProject* AvatarProject::openAvatarProject(const QString& path) {
return project;
}
AvatarProject* AvatarProject::createAvatarProject(const QString& avatarProjectName, const QString& avatarModelPath) {
AvatarProject* AvatarProject::createAvatarProject(const QString& projectsFolder, const QString& avatarProjectName, const QString& avatarModelPath, const QString& textureFolder) {
if (!isValidNewProjectName(avatarProjectName)) {
return nullptr;
}
QDir dir(getDefaultProjectsPath() + "/" + avatarProjectName);
if (!dir.mkpath(".")) {
QDir projectDir(projectsFolder + "/" + avatarProjectName);
if (!projectDir.mkpath(".")) {
return nullptr;
}
QDir projectTexturesDir(projectDir.path() + "/textures");
if (!projectTexturesDir.mkpath(".")) {
return nullptr;
}
QDir projectScriptsDir(projectDir.path() + "/scripts");
if (!projectScriptsDir.mkpath(".")) {
return nullptr;
}
const auto fileName = QFileInfo(avatarModelPath).fileName();
const auto newModelPath = dir.absoluteFilePath(fileName);
const auto newFSTPath = dir.absoluteFilePath("avatar.fst");
const auto newModelPath = projectDir.absoluteFilePath(fileName);
const auto newFSTPath = projectDir.absoluteFilePath("avatar.fst");
QFile::copy(avatarModelPath, newModelPath);
QFileInfo fbxInfo(newModelPath);
@ -66,10 +75,41 @@ AvatarProject* AvatarProject::createAvatarProject(const QString& avatarProjectNa
qDebug() << "Error reading: " << error;
return nullptr;
}
//TODO: copy/fix textures here:
QStringList textures{};
FST* fst = FST::createFSTFromModel(newFSTPath, newModelPath, *hfmModel);
auto addTextureToList = [&textures](hfm::Texture texture) mutable {
if (!texture.filename.isEmpty() && texture.content.isEmpty() && !textures.contains(texture.filename)) {
textures << texture.filename;
}
};
foreach(const HFMMaterial mat, hfmModel->materials) {
addTextureToList(mat.normalTexture);
addTextureToList(mat.albedoTexture);
addTextureToList(mat.opacityTexture);
addTextureToList(mat.glossTexture);
addTextureToList(mat.roughnessTexture);
addTextureToList(mat.specularTexture);
addTextureToList(mat.metallicTexture);
addTextureToList(mat.emissiveTexture);
addTextureToList(mat.occlusionTexture);
addTextureToList(mat.scatteringTexture);
addTextureToList(mat.lightmapTexture);
}
QDir textureDir(textureFolder.isEmpty() ? fbxInfo.absoluteDir() : textureFolder);
for (const auto& texture : textures) {
QString sourcePath = textureDir.path() + "/" + texture;
QString targetPath = projectTexturesDir.path() + "/" + texture;
QFileInfo sourceTexturePath(sourcePath);
if (sourceTexturePath.exists()) {
QFile::copy(sourcePath, targetPath);
}
}
auto fst = FST::createFSTFromModel(newFSTPath, newModelPath, *hfmModel);
fst->setName(avatarProjectName);
if (!fst->write()) {
@ -79,6 +119,22 @@ AvatarProject* AvatarProject::createAvatarProject(const QString& avatarProjectNa
return new AvatarProject(fst);
}
QStringList AvatarProject::getScriptPaths(const QDir& scriptsDir) {
QStringList result{};
constexpr auto flags = QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot | QDir::Hidden;
if (!scriptsDir.exists()) {
return result;
}
for (auto& script : scriptsDir.entryInfoList({}, flags)) {
if (script.fileName().endsWith(".js")) {
result.push_back("scripts/" + script.fileName());
}
}
return result;
}
bool AvatarProject::isValidNewProjectName(const QString& projectName) {
QDir dir(getDefaultProjectsPath() + "/" + projectName);
return !dir.exists();
@ -92,6 +148,9 @@ AvatarProject::AvatarProject(FST* fst) {
auto fileInfo = QFileInfo(getFSTPath());
_directory = fileInfo.absoluteDir();
_fst->setScriptPaths(getScriptPaths(QDir(_directory.path() + "/scripts")));
_fst->write();
//_projectFiles = _directory.entryList();
refreshProjectFiles();
@ -125,8 +184,7 @@ MarketplaceItemUploader* AvatarProject::upload(bool updateExisting) {
connect(uploader, &MarketplaceItemUploader::completed, this, [this, uploader]() {
if (uploader->getError() == MarketplaceItemUploader::Error::None) {
_fst->setMarketplaceID(uploader->getMarketplaceID());
// TODO(thoys) uncomment this
//_fst->write();
_fst->write();
}
});

View file

@ -36,11 +36,6 @@ class AvatarProject : public QObject {
Q_PROPERTY(QString name READ getProjectName)
public:
Q_INVOKABLE bool write() {
// Write FST here
return false;
}
Q_INVOKABLE MarketplaceItemUploader* upload(bool updateExisting);
Q_INVOKABLE void openInInventory();
@ -48,7 +43,8 @@ public:
* returns the AvatarProject or a nullptr on failure.
*/
static AvatarProject* openAvatarProject(const QString& path);
static AvatarProject* createAvatarProject(const QString& avatarProjectName, const QString& avatarModelPath);
static AvatarProject* createAvatarProject(const QString& projectsFolder, const QString& avatarProjectName,
const QString& avatarModelPath, const QString& textureFolder);
static bool isValidNewProjectName(const QString& projectName);
@ -61,7 +57,7 @@ private:
AvatarProject(FST* fst);
~AvatarProject() {
// TODO: cleanup FST / AvatarProjectUploader etc.
_fst->deleteLater();
}
Q_INVOKABLE QString getProjectName() const { return _fst->getName(); }
@ -73,6 +69,7 @@ private:
void refreshProjectFiles();
void appendDirectory(QString prefix, QDir dir);
QStringList getScriptPaths(const QDir& scriptsDir);
FST* _fst;

View file

@ -16,14 +16,23 @@
#include <hfm/HFM.h>
FST::FST(const QString& fstPath, QVariantHash data) : _fstPath(fstPath) {
if (data.contains(NAME_FIELD)) {
_name = data[NAME_FIELD].toString();
data.remove(NAME_FIELD);
}
if (data.contains(FILENAME_FIELD)) {
_modelPath = data[FILENAME_FIELD].toString();
data.remove(FILENAME_FIELD);
auto setValueFromFSTData = [&data] (const QString& propertyID, auto &targetProperty) mutable {
if (data.contains(propertyID)) {
targetProperty = data[propertyID].toString();
data.remove(propertyID);
}
};
setValueFromFSTData(NAME_FIELD, _name);
setValueFromFSTData(FILENAME_FIELD, _modelPath);
setValueFromFSTData(MARKETPLACE_ID_FIELD, _marketplaceID);
if (data.contains(SCRIPT_FIELD)) {
QVariantList scripts = data.values(SCRIPT_FIELD);
for (const auto& script : scripts) {
_scriptPaths.push_back(script.toString());
}
data.remove(SCRIPT_FIELD);
}
_other = data;
@ -42,10 +51,8 @@ FST* FST::createFSTFromModel(QString fstPath, QString modelFilePath, const hfm::
hfmModel.blendshapeChannelNames.contains("Squint_Right"));
mapping.insert(NAME_FIELD, QFileInfo(fstPath).baseName());
QDir root(modelFilePath);
mapping.insert(FILENAME_FIELD, root.relativeFilePath(fstPath));
mapping.insert(FILENAME_FIELD, QFileInfo(modelFilePath).fileName());
mapping.insert(TEXDIR_FIELD, "textures");
mapping.insert(SCRIPT_FIELD, "scripts");
// mixamo/autodesk defaults
mapping.insert(SCALE_FIELD, 1.0);
@ -150,9 +157,13 @@ void FST::setModelPath(const QString& modelPath) {
QVariantHash FST::getMapping() {
QVariantHash mapping;
mapping.insertMulti(NAME_FIELD, _name);
mapping.insertMulti(FILENAME_FIELD, _modelPath);
mapping.unite(_other);
mapping.insert(NAME_FIELD, _name);
mapping.insert(FILENAME_FIELD, _modelPath);
mapping.insert(MARKETPLACE_ID_FIELD, _marketplaceID);
for (const auto& scriptPath : _scriptPaths) {
mapping.insertMulti(SCRIPT_FIELD, scriptPath);
}
return mapping;
}

View file

@ -41,6 +41,9 @@ public:
QUuid getMarketplaceID() const { return _marketplaceID; }
void setMarketplaceID(QUuid marketplaceID) { _marketplaceID = marketplaceID; }
QStringList getScriptPaths() const { return _scriptPaths; }
void setScriptPaths(QStringList scriptPaths) { _scriptPaths = scriptPaths; }
QString getPath() { return _fstPath; }
QVariantHash getMapping();
@ -58,6 +61,8 @@ private:
QString _modelPath{};
QUuid _marketplaceID{};
QStringList _scriptPaths{};
QVariantHash _other{};
};

View file

@ -84,7 +84,7 @@ void FSTReader::writeVariant(QBuffer& buffer, QVariantHash::const_iterator& it)
QByteArray FSTReader::writeMapping(const QVariantHash& mapping) {
static const QStringList PREFERED_ORDER = QStringList() << NAME_FIELD << TYPE_FIELD << SCALE_FIELD << FILENAME_FIELD
<< TEXDIR_FIELD << SCRIPT_FIELD << JOINT_FIELD << FREE_JOINT_FIELD
<< MARKETPLACE_ID_FIELD << TEXDIR_FIELD << SCRIPT_FIELD << JOINT_FIELD << FREE_JOINT_FIELD
<< BLENDSHAPE_FIELD << JOINT_INDEX_FIELD;
QBuffer buffer;
buffer.open(QIODevice::WriteOnly);

View file

@ -18,6 +18,7 @@
static const QString NAME_FIELD = "name";
static const QString TYPE_FIELD = "type";
static const QString FILENAME_FIELD = "filename";
static const QString MARKETPLACE_ID_FIELD = "marketplaceID";
static const QString TEXDIR_FIELD = "texdir";
static const QString LOD_FIELD = "lod";
static const QString JOINT_INDEX_FIELD = "jointIndex";