From e0770f06b1eb27fcc5a8bd8db34ffb145bf0b3c5 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Wed, 25 Apr 2018 19:35:26 -0700 Subject: [PATCH] Add scripts to Model Packager --- interface/src/Application.cpp | 48 +++++++++---------- interface/src/Application.h | 3 ++ interface/src/ModelPackager.cpp | 30 ++++++++++-- interface/src/ModelPackager.h | 2 + interface/src/ModelPropertiesDialog.cpp | 19 ++++++++ interface/src/ModelPropertiesDialog.h | 2 + interface/src/avatar/MyAvatar.cpp | 20 ++++---- interface/src/avatar/MyAvatar.h | 9 ++-- libraries/fbx/src/FBX.h | 2 +- libraries/fbx/src/FSTReader.cpp | 4 +- .../src/model-networking/ModelCache.cpp | 23 +++++---- 11 files changed, 105 insertions(+), 57 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c1f77c792a..ad4cb56703 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2285,7 +2285,11 @@ void Application::onAboutToQuit() { // Hide Running Scripts dialog so that it gets destroyed in an orderly manner; prevents warnings at shutdown. DependencyManager::get()->hide("RunningScripts"); - + if (auto avatar = getMyAvatar()) { + auto urls = avatar->getScriptsToUnload(); + unloadAvatarScripts(urls); + } + _aboutToQuit = true; cleanupBeforeQuit(); @@ -4724,35 +4728,31 @@ void Application::init() { avatar->setCollisionSound(sound); } }, Qt::QueuedConnection); +} - connect(getMyAvatar().get(), &MyAvatar::avatarScriptsNeedToLoad, this, [this]() { - if (auto avatar = getMyAvatar()) { - auto scripts = avatar->getSkeletonModel()->getFBXGeometry().scripts; - if (scripts.size() > 0) { - auto scriptEngines = DependencyManager::get(); - auto runningScripts = scriptEngines->getRunningScripts(); - for (auto script : scripts) { - int index = runningScripts.indexOf(script.toString()); - if (index < 0) { - auto loaded = scriptEngines->loadScript(script); - avatar->addScriptToUnload(script); - } +void Application::loadAvatarScripts(const QVector& urls) { + if (auto avatar = getMyAvatar()) { + if (urls.size() > 0) { + auto scriptEngines = DependencyManager::get(); + auto runningScripts = scriptEngines->getRunningScripts(); + for (auto url : urls) { + int index = runningScripts.indexOf(url); + if (index < 0) { + scriptEngines->loadScript(url); + avatar->addScriptToUnload(url); } } } - }, Qt::QueuedConnection); + } +} - connect(getMyAvatar().get(), &MyAvatar::avatarScriptsNeedToUnload, this, [this]() { - if (auto avatar = getMyAvatar()) { - auto scripts = avatar->getScriptsToUnload(); - if (scripts.size() > 0) { - auto scriptEngines = DependencyManager::get(); - for (auto script : scripts) { - scriptEngines->stopScript(script.toString(), false); - } - } +void Application::unloadAvatarScripts(const QVector& urls) { + if (urls.size() > 0) { + auto scriptEngines = DependencyManager::get(); + for (auto url : urls) { + scriptEngines->stopScript(url, false); } - }, Qt::QueuedConnection); + } } void Application::updateLOD(float deltaTime) const { diff --git a/interface/src/Application.h b/interface/src/Application.h index 74b0e5a110..2e91f842a4 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -290,6 +290,9 @@ public: void replaceDomainContent(const QString& url); + void loadAvatarScripts(const QVector& urls); + void unloadAvatarScripts(const QVector& urls); + signals: void svoImportRequested(const QString& url); diff --git a/interface/src/ModelPackager.cpp b/interface/src/ModelPackager.cpp index 5f4c7526e0..a87669bf47 100644 --- a/interface/src/ModelPackager.cpp +++ b/interface/src/ModelPackager.cpp @@ -156,9 +156,11 @@ bool ModelPackager::zipModel() { QByteArray nameField = _mapping.value(NAME_FIELD).toByteArray(); tempDir.mkpath(nameField + "/textures"); + tempDir.mkpath(nameField + "/scripts"); QDir fbxDir(tempDir.path() + "/" + nameField); QDir texDir(fbxDir.path() + "/textures"); - + QDir scriptDir(fbxDir.path() + "/scripts"); + // Copy textures listTextures(); if (!_textures.empty()) { @@ -166,6 +168,22 @@ bool ModelPackager::zipModel() { _texDir = _modelFile.path() + "/" + texdirField; copyTextures(_texDir, texDir); } + + // Copy scripts + QByteArray scriptField = _mapping.value(SCRIPT_FIELD).toByteArray(); + _mapping.remove(SCRIPT_FIELD); + if (scriptField.size() > 1) { + tempDir.mkpath(nameField + "/scripts"); + _scriptDir = _modelFile.path() + "/" + scriptField; + QDir wdir = QDir(_scriptDir); + _mapping.remove(SCRIPT_FIELD); + auto list = wdir.entryList(QDir::NoDotAndDotDot | QDir::AllEntries); + for (auto script : list) { + auto sc = tempDir.relativeFilePath(scriptDir.path()) + "/" + QUrl(script).fileName(); + _mapping.insertMulti(SCRIPT_FIELD, sc); + } + copyDirectoryContent(wdir, scriptDir); + } // Copy LODs QVariantHash lodField = _mapping.value(LOD_FIELD).toHash(); @@ -189,7 +207,11 @@ bool ModelPackager::zipModel() { // Correct FST _mapping[FILENAME_FIELD] = tempDir.relativeFilePath(newPath); _mapping[TEXDIR_FIELD] = tempDir.relativeFilePath(texDir.path()); - + + for (auto multi : _mapping.values(SCRIPT_FIELD)) { + + multi.fromValue(tempDir.relativeFilePath(scriptDir.path()) + multi.toString()); + } // Copy FST QFile fst(tempDir.path() + "/" + nameField + ".fst"); if (fst.open(QIODevice::WriteOnly)) { @@ -237,7 +259,9 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename if (!mapping.contains(TEXDIR_FIELD)) { mapping.insert(TEXDIR_FIELD, "."); } - + if (!mapping.contains(SCRIPT_FIELD)) { + mapping.insert(SCRIPT_FIELD, "."); + } // mixamo/autodesk defaults if (!mapping.contains(SCALE_FIELD)) { mapping.insert(SCALE_FIELD, 1.0); diff --git a/interface/src/ModelPackager.h b/interface/src/ModelPackager.h index 10942833f9..60b3825c4d 100644 --- a/interface/src/ModelPackager.h +++ b/interface/src/ModelPackager.h @@ -37,10 +37,12 @@ private: QFileInfo _fbxInfo; FSTReader::ModelType _modelType; QString _texDir; + QString _scriptDir; QVariantHash _mapping; std::unique_ptr _geometry; QStringList _textures; + QStringList _scripts; }; diff --git a/interface/src/ModelPropertiesDialog.cpp b/interface/src/ModelPropertiesDialog.cpp index ae352974ae..35b07aa2b2 100644 --- a/interface/src/ModelPropertiesDialog.cpp +++ b/interface/src/ModelPropertiesDialog.cpp @@ -43,6 +43,9 @@ _geometry(geometry) form->addRow("Texture Directory:", _textureDirectory = new QPushButton()); connect(_textureDirectory, SIGNAL(clicked(bool)), SLOT(chooseTextureDirectory())); + form->addRow("Script Directory:", _scriptDirectory = new QPushButton()); + connect(_scriptDirectory, SIGNAL(clicked(bool)), SLOT(chooseScriptDirectory())); + form->addRow("Scale:", _scale = new QDoubleSpinBox()); _scale->setMaximum(FLT_MAX); _scale->setSingleStep(0.01); @@ -100,6 +103,7 @@ QVariantHash ModelPropertiesDialog::getMapping() const { mapping.insert(TYPE_FIELD, getType()); mapping.insert(NAME_FIELD, _name->text()); mapping.insert(TEXDIR_FIELD, _textureDirectory->text()); + mapping.insert(SCRIPT_FIELD, _scriptDirectory->text()); mapping.insert(SCALE_FIELD, QString::number(_scale->value())); // update the joint indices @@ -157,6 +161,7 @@ void ModelPropertiesDialog::reset() { _name->setText(_originalMapping.value(NAME_FIELD).toString()); _textureDirectory->setText(_originalMapping.value(TEXDIR_FIELD).toString()); _scale->setValue(_originalMapping.value(SCALE_FIELD).toDouble()); + _scriptDirectory->setText(_originalMapping.value(SCRIPT_FIELD).toString()); QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash(); @@ -207,6 +212,20 @@ void ModelPropertiesDialog::chooseTextureDirectory() { _textureDirectory->setText(directory.length() == _basePath.length() ? "." : directory.mid(_basePath.length() + 1)); } +void ModelPropertiesDialog::chooseScriptDirectory() { + QString directory = QFileDialog::getExistingDirectory(this, "Choose Script Directory", + _basePath + "/" + _scriptDirectory->text()); + if (directory.isEmpty()) { + return; + } + if (!directory.startsWith(_basePath)) { + OffscreenUi::asyncWarning(NULL, "Invalid script directory", "Script directory must be child of base path."); + return; + } + _scriptDirectory->setText(directory.length() == _basePath.length() ? "." : directory.mid(_basePath.length() + 1)); +} + + void ModelPropertiesDialog::updatePivotJoint() { _pivotJoint->setEnabled(!_pivotAboutCenter->isChecked()); } diff --git a/interface/src/ModelPropertiesDialog.h b/interface/src/ModelPropertiesDialog.h index 11abc5ab54..e3c2d8ed6a 100644 --- a/interface/src/ModelPropertiesDialog.h +++ b/interface/src/ModelPropertiesDialog.h @@ -37,6 +37,7 @@ public: private slots: void reset(); void chooseTextureDirectory(); + void chooseScriptDirectory(); void updatePivotJoint(); void createNewFreeJoint(const QString& joint = QString()); @@ -52,6 +53,7 @@ private: FBXGeometry _geometry; QLineEdit* _name = nullptr; QPushButton* _textureDirectory = nullptr; + QPushButton* _scriptDirectory = nullptr; QDoubleSpinBox* _scale = nullptr; QDoubleSpinBox* _translationX = nullptr; QDoubleSpinBox* _translationY = nullptr; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 2ba4a6afca..69acf26477 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -121,7 +121,12 @@ MyAvatar::MyAvatar(QThread* thread) : _skeletonModel = std::make_shared(this, nullptr); connect(_skeletonModel.get(), &Model::setURLFinished, this, &Avatar::setModelURLFinished); - connect(_skeletonModel.get(), &Model::setURLFinished, this, &MyAvatar::setModelURLLoaded); + connect(_skeletonModel.get(), &Model::setURLFinished, this, [this](bool success) { + if (success) { + auto geometry = getSkeletonModel()->getFBXGeometry(); + qApp->loadAvatarScripts(geometry.scripts); + } + }); connect(_skeletonModel.get(), &Model::rigReady, this, &Avatar::rigReady); connect(_skeletonModel.get(), &Model::rigReset, this, &Avatar::rigReset); @@ -1464,9 +1469,7 @@ void MyAvatar::clearJointsData() { } void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { - if (_scriptsToUnload.size() > 0) { - emit avatarScriptsNeedToUnload(); - } + qApp->unloadAvatarScripts(_scriptsToUnload); _skeletonModelChangeCount++; int skeletonModelChangeCount = _skeletonModelChangeCount; Avatar::setSkeletonModelURL(skeletonModelURL); @@ -2388,11 +2391,6 @@ void MyAvatar::restrictScaleFromDomainSettings(const QJsonObject& domainSettings settings.endGroup(); } -void MyAvatar::setModelURLLoaded() { - _scriptsToUnload.clear(); - emit avatarScriptsNeedToLoad(); -} - void MyAvatar::leaveDomain() { clearScaleRestriction(); saveAvatarScale(); @@ -2840,8 +2838,8 @@ float MyAvatar::getWalkSpeed() const { return _walkSpeed.get() * _walkSpeedScalar; } -void MyAvatar::addScriptToUnload(QUrl& scriptUrl) { - _scriptsToUnload.push_back(scriptUrl); +void MyAvatar::addScriptToUnload(QString& url) { + _scriptsToUnload.push_back(url); } void MyAvatar::setSprintMode(bool sprint) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 38e189f92b..6e67defe6f 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -594,8 +594,8 @@ public: void setWalkSpeed(float value); float getWalkSpeed() const; - void addScriptToUnload(QUrl& scriptUrl); - const QVector& getScriptsToUnload() const { return _scriptsToUnload; }; + void addScriptToUnload(QString& url); + const QVector& getScriptsToUnload() const { return _scriptsToUnload; }; public slots: void increaseSize(); @@ -662,12 +662,9 @@ signals: void sensorToWorldScaleChanged(float sensorToWorldScale); void attachmentsChanged(); void scaleChanged(); - void avatarScriptsNeedToLoad(); - void avatarScriptsNeedToUnload(); private slots: void leaveDomain(); - void setModelURLLoaded(); protected: virtual void beParentOfChild(SpatiallyNestablePointer newChild) const override; @@ -911,7 +908,7 @@ private: ThreadSafeValueCache _walkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED }; float _walkSpeedScalar { AVATAR_WALK_SPEED_SCALAR }; - QVector _scriptsToUnload; + QVector _scriptsToUnload; }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index d40511ce27..ce3fc52c3a 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -298,7 +298,7 @@ public: bool hasSkeletonJoints; QVector meshes; - QVector scripts; + QVector scripts; QHash materials; diff --git a/libraries/fbx/src/FSTReader.cpp b/libraries/fbx/src/FSTReader.cpp index cc4a919445..603f214c3e 100644 --- a/libraries/fbx/src/FSTReader.cpp +++ b/libraries/fbx/src/FSTReader.cpp @@ -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 << JOINT_FIELD << FREE_JOINT_FIELD + << TEXDIR_FIELD << SCRIPT_FIELD << JOINT_FIELD << FREE_JOINT_FIELD << BLENDSHAPE_FIELD << JOINT_INDEX_FIELD; QBuffer buffer; buffer.open(QIODevice::WriteOnly); @@ -92,7 +92,7 @@ QByteArray FSTReader::writeMapping(const QVariantHash& mapping) { for (auto key : PREFERED_ORDER) { auto it = mapping.find(key); if (it != mapping.constEnd()) { - if (key == FREE_JOINT_FIELD) { // writeVariant does not handle strings added using insertMulti. + if (key == FREE_JOINT_FIELD || key == SCRIPT_FIELD) { // writeVariant does not handle strings added using insertMulti. for (auto multi : mapping.values(key)) { buffer.write(key.toUtf8()); buffer.write(" = "); diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 5a25bbc6fd..e3f543c403 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -210,16 +210,19 @@ void GeometryReader::run() { throw QString("unsupported format"); } - if (_mapping.value("type").toString() == "body+head") { - auto scripts = _mapping.value("script"); - if (!scripts.isNull()) { - auto scriptsMap = scripts.toMap(); - auto count = scriptsMap.size(); - if (count > 0) { - for (auto &key : scriptsMap.keys()) { - auto scriptUrl = scriptsMap[key].toString(); - fbxGeometry->scripts.push_back(QUrl(scriptUrl)); - } + // Store fst scripts on geometry + if (!_mapping.value(SCRIPT_FIELD).isNull()) { + QVariantList scripts = _mapping.values(SCRIPT_FIELD); + if (scripts.size() > 0) { + for (auto &script : scripts) { + QString scriptUrl = script.toString(); + if (QUrl(scriptUrl).isRelative()) { + if (scriptUrl.at(0) == '/') { + scriptUrl = scriptUrl.right(scriptUrl.length() - 1); + } + scriptUrl = _url.resolved(QUrl(scriptUrl)).toString(); + } + fbxGeometry->scripts.push_back(scriptUrl); } } }