summaryrefslogtreecommitdiffstats
path: root/editorlib/src/editorscenesaver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'editorlib/src/editorscenesaver.cpp')
-rw-r--r--editorlib/src/editorscenesaver.cpp244
1 files changed, 244 insertions, 0 deletions
diff --git a/editorlib/src/editorscenesaver.cpp b/editorlib/src/editorscenesaver.cpp
new file mode 100644
index 0000000..2e128f6
--- /dev/null
+++ b/editorlib/src/editorscenesaver.cpp
@@ -0,0 +1,244 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D Editor of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "editorscenesaver.h"
+#include "editorutils.h"
+
+#include <Qt3DCore/qentity.h>
+#include <private/qsceneexportfactory_p.h>
+#include <private/qsceneexporter_p.h>
+#include <private/qsceneimportfactory_p.h>
+#include <private/qsceneimporter_p.h>
+
+#include <QtCore/qfile.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qtemporarydir.h>
+#include <QtCore/qtemporaryfile.h>
+#include <QtCore/qiodevice.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qdatastream.h>
+#include <QtCore/qdatetime.h>
+
+namespace {
+
+const QString autoSavePostfix = QStringLiteral("_autosave");
+const QString saveSuffix = QStringLiteral(".qt3dscene");
+const QString exportNameTemplate = QStringLiteral("%1_scene_res");
+const QString editorDataFile = QStringLiteral("qt3dscene_editor_data.json");
+const qint32 saveFileVersion = 1;
+const QByteArray saveFileId = QByteArrayLiteral("Qt3DSceneEditor_SaveFile");
+const QString activeCameraKey = QStringLiteral("activeCamera");
+const QString rootEntityNameKey = QStringLiteral("rootEntityName");
+
+} // namespace
+
+EditorSceneSaver::EditorSceneSaver(QObject *parent)
+ : QObject(parent)
+ , m_loadDir(nullptr)
+{
+}
+
+EditorSceneSaver::~EditorSceneSaver()
+{
+ delete m_loadDir;
+}
+
+bool EditorSceneSaver::saveScene(Qt3DCore::QEntity *sceneEntity,
+ const QString &activeSceneCamera,
+ const QString &saveFileName,
+ bool autosave)
+{
+ // Save consists of a single JSON file with .qt3dscene extension and the exported QGLTF scene
+ // in a separate subfolder in the same folder as the main save file.
+
+ // The save is first created in a temp directory to ensure saving over existing save doesn't
+ // partially overwrite the old save in case of error.
+ QString finalFullSaveFilePathName = saveFileName;
+ if (!finalFullSaveFilePathName.endsWith(saveSuffix))
+ finalFullSaveFilePathName.append(saveSuffix);
+ QFileInfo saveFileInfo(finalFullSaveFilePathName);
+ QString finalSavePath = saveFileInfo.path();
+ QString finalFileName = saveFileInfo.fileName();
+ QString saveExportName = exportNameTemplate.arg(saveFileInfo.completeBaseName());
+ QDir finalSaveDir = saveFileInfo.absoluteDir();
+ if (autosave) {
+ finalFullSaveFilePathName.append(autoSavePostfix);
+ finalFileName.append(autoSavePostfix);
+ saveExportName.append(autoSavePostfix);
+ }
+ QString finalScenePath = finalSavePath + QStringLiteral("/") + saveExportName;
+
+ // Save scene to a temp folder using GLTF export plugin
+ QTemporaryDir exportDir(finalScenePath + QStringLiteral("_temp_save_XXXXXX"));
+
+ const QStringList keys = Qt3DRender::QSceneExportFactory::keys();
+ for (const QString &key : keys) {
+ Qt3DRender::QSceneExporter *exporter =
+ Qt3DRender::QSceneExportFactory::create(key, QStringList());
+ if (exporter != nullptr && key == QStringLiteral("gltfexport")) {
+ QVariantHash options;
+ if (!exporter->exportScene(sceneEntity, exportDir.path(), saveExportName, options)) {
+ qWarning() << "Failed to export GLTF when saving the scene";
+ return false;
+ }
+ break;
+ }
+ }
+
+ // Create new save file in the temp folder
+ QJsonDocument jsonDoc;
+ QJsonObject editorData;
+ editorData[activeCameraKey] = activeSceneCamera;
+ editorData[rootEntityNameKey] = sceneEntity->objectName();
+ jsonDoc.setObject(editorData);
+ const QString tempScenePath = exportDir.path() + QStringLiteral("/") + saveExportName;
+ const QString saveFilePath = exportDir.path() + QStringLiteral("/") + finalFileName;
+ QFile saveFile(saveFilePath);
+ if (saveFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+ QByteArray json = jsonDoc.toJson();
+ saveFile.write(json);
+ saveFile.close();
+ } else {
+ qWarning() << "Failed to create editor data file when saving the scene";
+ return false;
+ }
+
+ // Create a unique backup suffix from current time
+ QDateTime currentTime = QDateTime::currentDateTime();
+ QString uniqueSuffix = currentTime.toString(QStringLiteral("yyyyMMddHHmmsszzz"));
+ QString backupExportName = saveExportName + uniqueSuffix;
+ QString backupSaveFileName = finalFullSaveFilePathName + uniqueSuffix;
+
+ // Rename the old save file and exported resources and savefile
+ if (finalSaveDir.exists(saveExportName)) {
+ if (!finalSaveDir.rename(saveExportName, backupExportName)) {
+ qWarning() << "Failed to rename the old resource dir:" << saveExportName;
+ return false;
+ }
+ }
+ QFile oldSaveFile(finalFullSaveFilePathName);
+ if (oldSaveFile.exists()) {
+ if (!oldSaveFile.rename(backupSaveFileName)) {
+ qWarning() << "Failed to rename the old save file:" << finalFullSaveFilePathName;
+ finalSaveDir.rename(backupExportName, saveExportName);
+ return false;
+ }
+ }
+
+ // Rename the temporary files as finals
+ if (!QFile::rename(tempScenePath, finalScenePath)) {
+ qWarning() << "Failed to rename the temp scene:" << tempScenePath << "->" << finalScenePath;
+ return false;
+ }
+ if (!saveFile.rename(finalFullSaveFilePathName)) {
+ qWarning() << "Failed to rename the temp save file:" << saveFilePath <<
+ "->" << finalFullSaveFilePathName;
+ return false;
+ }
+
+ // If everything went well, remove the renamed originals.
+ QFile::remove(backupSaveFileName);
+ if (finalSaveDir.cd(backupExportName))
+ finalSaveDir.removeRecursively();
+
+ return true;
+}
+
+Qt3DCore::QEntity *EditorSceneSaver::loadScene(const QString &fileName, Qt3DCore::QEntity *camera)
+{
+ Qt3DCore::QEntity *loadedScene = nullptr;
+ camera = nullptr;
+
+ // Read the scene data
+ QFile jsonFile(fileName);
+ QString activeCameraId;
+ QString sceneName;
+ if (jsonFile.open(QIODevice::ReadOnly)) {
+ QByteArray jsonData = jsonFile.readAll();
+ jsonFile.close();
+ QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonData);
+ QJsonObject editorData = jsonDocument.object();
+ activeCameraId = editorData.value(activeCameraKey).toString();
+ sceneName = editorData.value(rootEntityNameKey).toString();
+ } else {
+ qWarning() << "Failed to open scene save file:" << fileName;
+ return nullptr;
+ }
+
+ // Import scene from temp folder
+ const QStringList keys = Qt3DRender::QSceneImportFactory::keys();
+ for (const QString &key : keys) {
+ Qt3DRender::QSceneImporter *importer =
+ Qt3DRender::QSceneImportFactory::create(key, QStringList());
+ if (importer != nullptr && key == QStringLiteral("gltf")) {
+ QFileInfo saveFileInfo(fileName);
+ const QString exportName = exportNameTemplate.arg(saveFileInfo.completeBaseName());
+ QString sceneSource = saveFileInfo.absolutePath();
+ sceneSource += QStringLiteral("/");
+ sceneSource += exportName;
+ sceneSource += QStringLiteral("/");
+ sceneSource += exportName;
+ sceneSource += QStringLiteral(".qgltf");
+ importer->setSource(QUrl::fromLocalFile(sceneSource));
+ loadedScene = importer->scene();
+ break;
+ }
+ }
+
+ if (!loadedScene) {
+ qWarning() << "Failed to load the saved scene";
+ return nullptr;
+ }
+
+ loadedScene->setObjectName(sceneName);
+
+ // Find the active camera
+ camera = EditorUtils::findEntityByName(loadedScene, activeCameraId);
+ if (!EditorUtils::entityCameraLens(camera))
+ camera = nullptr;
+
+ return loadedScene;
+}
+
+void EditorSceneSaver::deleteSavedScene(const QString &saveFileName, bool autosave)
+{
+ QString fullSaveFilePathName = saveFileName;
+ if (!fullSaveFilePathName.endsWith(saveSuffix))
+ fullSaveFilePathName.append(saveSuffix);
+ QFileInfo saveFileInfo(fullSaveFilePathName);
+ QString savePath = saveFileInfo.path();
+ QString saveExportName = exportNameTemplate.arg(saveFileInfo.completeBaseName());
+ if (autosave) {
+ fullSaveFilePathName.append(autoSavePostfix);
+ saveExportName.append(autoSavePostfix);
+ }
+ QDir sceneResourcesDir(savePath + QStringLiteral("/") + saveExportName);
+ sceneResourcesDir.removeRecursively();
+ QFile::remove(fullSaveFilePathName);
+}