239 lines · 7.8 KB · C++
RawDownload
////  AvatarProject.cpp//////  Created by Thijs Wenker on 12/7/2018//  Copyright 2018 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 "AvatarProject.h" #include <FSTReader.h> #include <QFile>#include <QFileInfo>#include <QQmlEngine>#include <QTimer> #include "FBXSerializer.h"#include <ui/TabletScriptingInterface.h>#include "scripting/HMDScriptingInterface.h" AvatarProject* AvatarProject::openAvatarProject(const QString& path, AvatarProjectStatus::AvatarProjectStatus& status) {    status = AvatarProjectStatus::NONE;     if (!path.toLower().endsWith(".fst")) {        status = AvatarProjectStatus::ERROR_OPEN_INVALID_FILE_TYPE;        return nullptr;    }     QFileInfo fstFileInfo{ path };    if (!fstFileInfo.absoluteDir().exists()) {        status = AvatarProjectStatus::ERROR_OPEN_PROJECT_FOLDER;        return nullptr;    }     if (!fstFileInfo.exists()) {        status = AvatarProjectStatus::ERROR_OPEN_FIND_FST;        return nullptr;    }     QFile file{ fstFileInfo.filePath() };    if (!file.open(QIODevice::ReadOnly)) {        status = AvatarProjectStatus::ERROR_OPEN_OPEN_FST;        return nullptr;    }     const auto project = new AvatarProject(path, file.readAll());     QFileInfo fbxFileInfo{ project->getFBXPath() };    if (!fbxFileInfo.exists()) {        project->deleteLater();        status = AvatarProjectStatus::ERROR_OPEN_FIND_MODEL;        return nullptr;    }     QQmlEngine::setObjectOwnership(project, QQmlEngine::CppOwnership);    status = AvatarProjectStatus::SUCCESS;    return project;} AvatarProject* AvatarProject::createAvatarProject(const QString& projectsFolder, const QString& avatarProjectName,                                                  const QString& avatarModelPath, const QString& textureFolder,                                                  AvatarProjectStatus::AvatarProjectStatus& status) {    status = AvatarProjectStatus::NONE;     if (!isValidNewProjectName(projectsFolder, avatarProjectName)) {        status = AvatarProjectStatus::ERROR_CREATE_PROJECT_NAME;        return nullptr;    }     QDir projectDir(projectsFolder + "/" + avatarProjectName);    if (!projectDir.mkpath(".")) {        status = AvatarProjectStatus::ERROR_CREATE_CREATING_DIRECTORIES;        return nullptr;    }     QDir projectTexturesDir(projectDir.path() + "/textures");    if (!projectTexturesDir.mkpath(".")) {        status = AvatarProjectStatus::ERROR_CREATE_CREATING_DIRECTORIES;        return nullptr;    }     QDir projectScriptsDir(projectDir.path() + "/scripts");    if (!projectScriptsDir.mkpath(".")) {        status = AvatarProjectStatus::ERROR_CREATE_CREATING_DIRECTORIES;        return nullptr;    }     const auto fileName = QFileInfo(avatarModelPath).fileName();    const auto newModelPath = projectDir.absoluteFilePath(fileName);    const auto newFSTPath = projectDir.absoluteFilePath("avatar.fst");    QFile::copy(avatarModelPath, newModelPath);     QFileInfo fbxInfo{ newModelPath };    if (!fbxInfo.exists() || !fbxInfo.isFile()) {        status = AvatarProjectStatus::ERROR_CREATE_FIND_MODEL;        return nullptr;    }     QFile fbx{ fbxInfo.filePath() };    if (!fbx.open(QIODevice::ReadOnly)) {        status = AvatarProjectStatus::ERROR_CREATE_OPEN_MODEL;        return nullptr;    }     std::shared_ptr<hfm::Model> hfmModel;     try {        const QByteArray fbxContents = fbx.readAll();        hfmModel = FBXSerializer().read(fbxContents, QVariantHash(), fbxInfo.filePath());    } catch (const QString& error) {        Q_UNUSED(error)        status = AvatarProjectStatus::ERROR_CREATE_READ_MODEL;        return nullptr;    }    QStringList textures{};     auto addTextureToList = [&textures](hfm::Texture texture) mutable {        if (!texture.filename.isEmpty() && texture.content.isEmpty() && !textures.contains(texture.filename)) {            textures << texture.filename;        }    };     foreach(const HFMMaterial material, hfmModel->materials) {        addTextureToList(material.normalTexture);        addTextureToList(material.albedoTexture);        addTextureToList(material.opacityTexture);        addTextureToList(material.glossTexture);        addTextureToList(material.roughnessTexture);        addTextureToList(material.specularTexture);        addTextureToList(material.metallicTexture);        addTextureToList(material.emissiveTexture);        addTextureToList(material.occlusionTexture);        addTextureToList(material.scatteringTexture);        addTextureToList(material.lightmapTexture);         if (material.isMToonMaterial) {            addTextureToList(material.shadeTexture);            addTextureToList(material.shadingShiftTexture);            addTextureToList(material.matcapTexture);            addTextureToList(material.rimTexture);            addTextureToList(material.uvAnimationTexture);        }    }     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()) {        status = AvatarProjectStatus::ERROR_CREATE_WRITE_FST;        return nullptr;    }     status = AvatarProjectStatus::SUCCESS;    return new AvatarProject(fst);} QStringList AvatarProject::getScriptPaths(const QDir& scriptsDir) const {    QStringList result{};    constexpr auto flags = QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot | QDir::Hidden;    if (!scriptsDir.exists()) {        return result;    }     for (const auto& script : scriptsDir.entryInfoList({}, flags)) {        if (script.fileName().toLower().endsWith(".js")) {            result.push_back("scripts/" + script.fileName());        }    }     return result;} bool AvatarProject::isValidNewProjectName(const QString& projectPath, const QString& projectName) {    if (projectPath.trimmed().isEmpty() || projectName.trimmed().isEmpty()) {        return false;    }    QDir dir(projectPath + "/" + projectName);    return !dir.exists();} AvatarProject::AvatarProject(const QString& fstPath, const QByteArray& data) :    AvatarProject::AvatarProject(new FST(fstPath, FSTReader::readMapping(data))) {}AvatarProject::AvatarProject(FST* fst) {    _fst = fst;    auto fileInfo = QFileInfo(getFSTPath());    _directory = fileInfo.absoluteDir();     _fst->setScriptPaths(getScriptPaths(QDir(_directory.path() + "/scripts")));    _fst->write();     refreshProjectFiles();     _projectPath = fileInfo.absoluteDir().absolutePath();} void AvatarProject::appendDirectory(const QString& prefix, const QDir& dir) {    constexpr auto flags = QDir::Dirs | QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot | QDir::Hidden;    for (auto& entry : dir.entryInfoList({}, flags)) {        if (entry.isFile()) {            _projectFiles.append({ entry.absoluteFilePath(), prefix + entry.fileName() });        } else if (entry.isDir()) {            appendDirectory(prefix + entry.fileName() + "/", entry.absoluteFilePath());        }    }} void AvatarProject::refreshProjectFiles() {    _projectFiles.clear();    appendDirectory("", _directory);} QStringList AvatarProject::getProjectFiles() const {    QStringList paths;    for (auto& path : _projectFiles) {        paths.append(path.relativePath);     }    return paths;} AvatarDoctor* AvatarProject::diagnose() {    return new AvatarDoctor(QUrl(getFSTPath()));}