add a simple domain baker to enumerate models.json.gz

This commit is contained in:
Stephen Birarda 2017-04-12 16:46:05 -07:00
parent 074fb08313
commit 177d4d0e07
7 changed files with 391 additions and 2 deletions

View file

@ -0,0 +1,111 @@
//
// DomainBaker.cpp
// tools/oven/src
//
// Created by Stephen Birarda on 4/12/17.
// Copyright 2017 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 <QtCore/QDebug>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include "Gzip.h"
#include "DomainBaker.h"
DomainBaker::DomainBaker(const QUrl& localModelFileURL, QString baseOutputPath) :
_localEntitiesFileURL(localModelFileURL),
_baseOutputPath(baseOutputPath)
{
}
void DomainBaker::start() {
loadLocalFile();
enumerateEntities();
}
void DomainBaker::loadLocalFile() {
// load up the local entities file
QFile modelsFile { _localEntitiesFileURL.toLocalFile() };
if (!modelsFile.open(QIODevice::ReadOnly)) {
// add an error to our list to specify that the file could not be read
// return to stop processing
return;
}
// grab a byte array from the file
auto fileContents = modelsFile.readAll();
// check if we need to inflate a gzipped models file or if this was already decompressed
static const QString GZIPPED_ENTITIES_FILE_SUFFIX = "gz";
if (QFileInfo(_localEntitiesFileURL.toLocalFile()).suffix() == "gz") {
// this was a gzipped models file that we need to decompress
QByteArray uncompressedContents;
gunzip(fileContents, uncompressedContents);
fileContents = uncompressedContents;
}
// read the file contents to a JSON document
auto jsonDocument = QJsonDocument::fromJson(fileContents);
// grab the entities object from the root JSON object
_entities = jsonDocument.object()["Entities"].toArray();
if (_entities.isEmpty()) {
// add an error to our list stating that the models file was empty
// return to stop processing
return;
}
}
void DomainBaker::enumerateEntities() {
qDebug() << "Enumerating" << _entities.size() << "entities from domain";
foreach(QJsonValue entityValue, _entities) {
// make sure this is a JSON object
if (entityValue.isObject()) {
auto entity = entityValue.toObject();
// check if this is an entity with a model URL
static const QString ENTITY_MODEL_URL_KEY = "modelURL";
if (entity.contains(ENTITY_MODEL_URL_KEY)) {
// grab a QUrl for the model URL
auto modelURL = QUrl(entity[ENTITY_MODEL_URL_KEY].toString());
// check if the file pointed to by this URL is a bakeable model, by comparing extensions
auto modelFileName = modelURL.fileName();
static const QStringList BAKEABLE_MODEL_EXTENSIONS { ".fbx" };
auto completeLowerExtension = modelFileName.mid(modelFileName.indexOf('.')).toLower();
if (BAKEABLE_MODEL_EXTENSIONS.contains(completeLowerExtension)) {
// grab a clean version of the URL without a query or fragment
modelURL.setFragment("");
modelURL.setQuery("");
// setup an FBXBaker for this URL, as long as we don't already have one
if (!_bakers.contains(modelURL)) {
QSharedPointer<FBXBaker> baker { new FBXBaker(modelURL, _baseOutputPath) };
// start the baker
baker->start();
// insert it into our bakers hash so we hold a strong pointer to it
_bakers.insert(modelURL, baker);
}
}
}
}
}
}

View file

@ -0,0 +1,44 @@
//
// DomainBaker.h
// tools/oven/src
//
// Created by Stephen Birarda on 4/12/17.
// Copyright 2017 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
//
#ifndef hifi_DomainBaker_h
#define hifi_DomainBaker_h
#include <QtCore/QJsonArray>
#include <QtCore/QObject>
#include <QtCore/QUrl>
#include <FBXBaker.h>
class DomainBaker : public QObject {
Q_OBJECT
public:
DomainBaker(const QUrl& localEntitiesFileURL, QString baseOutputPath);
public slots:
void start();
signals:
void finished();
private:
void loadLocalFile();
void enumerateEntities();
QUrl _localEntitiesFileURL;
QString _baseOutputPath;
QJsonArray _entities;
QHash<QUrl, QSharedPointer<FBXBaker>> _bakers;
};
#endif // hifi_DomainBaker_h

View file

@ -0,0 +1,177 @@
//
// DomainBakeWidget.cpp
// tools/oven/src/ui
//
// Created by Stephen Birarda on 4/12/17.
// Copyright 2017 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 <QtWidgets/QFileDialog>
#include <QtWidgets/QGridLayout>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QStackedWidget>
#include <QtCore/QDir>
#include <QtCore/QDebug>
#include "DomainBakeWidget.h"
static const QString EXPORT_DIR_SETTING_KEY = "domain_export_directory";
static const QString BROWSE_START_DIR_SETTING_KEY = "domain_search_directory";
DomainBakeWidget::DomainBakeWidget(QWidget* parent, Qt::WindowFlags flags) :
QWidget(parent, flags),
_exportDirectory(EXPORT_DIR_SETTING_KEY),
_browseStartDirectory(BROWSE_START_DIR_SETTING_KEY)
{
setupUI();
}
void DomainBakeWidget::setupUI() {
// setup a grid layout to hold everything
QGridLayout* gridLayout = new QGridLayout;
int rowIndex = 0;
// setup a section to choose the file being baked
QLabel* entitiesFileLabel = new QLabel("Entities File");
_entitiesFileLineEdit = new QLineEdit;
_entitiesFileLineEdit->setPlaceholderText("File or URL");
QPushButton* chooseFileButton = new QPushButton("Browse...");
connect(chooseFileButton, &QPushButton::clicked, this, &DomainBakeWidget::chooseFileButtonClicked);
// add the components for the entities file picker to the layout
gridLayout->addWidget(entitiesFileLabel, rowIndex, 0);
gridLayout->addWidget(_entitiesFileLineEdit, rowIndex, 1, 1, 3);
gridLayout->addWidget(chooseFileButton, rowIndex, 4);
// start a new row for next component
++rowIndex;
// setup a section to choose the output directory
QLabel* outputDirectoryLabel = new QLabel("Output Directory");
_outputDirLineEdit = new QLineEdit;
// set the current export directory to whatever was last used
_outputDirLineEdit->setText(_exportDirectory.get());
// whenever the output directory line edit changes, update the value in settings
connect(_outputDirLineEdit, &QLineEdit::textChanged, this, &DomainBakeWidget::outputDirectoryChanged);
QPushButton* chooseOutputDirectoryButton = new QPushButton("Browse...");
connect(chooseOutputDirectoryButton, &QPushButton::clicked, this, &DomainBakeWidget::chooseOutputDirButtonClicked);
// add the components for the output directory picker to the layout
gridLayout->addWidget(outputDirectoryLabel, rowIndex, 0);
gridLayout->addWidget(_outputDirLineEdit, rowIndex, 1, 1, 3);
gridLayout->addWidget(chooseOutputDirectoryButton, rowIndex, 4);
// start a new row for the next component
++rowIndex;
// add a horizontal line to split the bake/cancel buttons off
QFrame* lineFrame = new QFrame;
lineFrame->setFrameShape(QFrame::HLine);
lineFrame->setFrameShadow(QFrame::Sunken);
gridLayout->addWidget(lineFrame, rowIndex, 0, 1, -1);
// start a new row for the next component
++rowIndex;
// add a button that will kickoff the bake
QPushButton* bakeButton = new QPushButton("Bake");
connect(bakeButton, &QPushButton::clicked, this, &DomainBakeWidget::bakeButtonClicked);
gridLayout->addWidget(bakeButton, rowIndex, 3);
// add a cancel button to go back to the modes page
QPushButton* cancelButton = new QPushButton("Cancel");
connect(cancelButton, &QPushButton::clicked, this, &DomainBakeWidget::cancelButtonClicked);
gridLayout->addWidget(cancelButton, rowIndex, 4);
setLayout(gridLayout);
}
void DomainBakeWidget::chooseFileButtonClicked() {
// pop a file dialog so the user can select the entities file
// if we have picked an FBX before, start in the folder that matches the last path
// otherwise start in the home directory
auto startDir = _browseStartDirectory.get();
if (startDir.isEmpty()) {
startDir = QDir::homePath();
}
auto selectedFile = QFileDialog::getOpenFileName(this, "Choose Entities File", startDir);
if (!selectedFile.isEmpty()) {
// set the contents of the entities file text box to be the path to the selected file
_entitiesFileLineEdit->setText(selectedFile);
auto directoryOfEntitiesFile = QFileInfo(selectedFile).absolutePath();
// save the directory containing this entities file so we can default to it next time we show the file dialog
_browseStartDirectory.set(directoryOfEntitiesFile);
// if our output directory is not yet set, set it to the directory of this entities file
_outputDirLineEdit->setText(directoryOfEntitiesFile);
}
}
void DomainBakeWidget::chooseOutputDirButtonClicked() {
// pop a file dialog so the user can select the output directory
// if we have a previously selected output directory, use that as the initial path in the choose dialog
// otherwise use the user's home directory
auto startDir = _exportDirectory.get();
if (startDir.isEmpty()) {
startDir = QDir::homePath();
}
auto selectedDir = QFileDialog::getExistingDirectory(this, "Choose Output Directory", startDir);
if (!selectedDir.isEmpty()) {
// set the contents of the output directory text box to be the path to the directory
_outputDirLineEdit->setText(selectedDir);
}
}
void DomainBakeWidget::outputDirectoryChanged(const QString& newDirectory) {
// update the export directory setting so we can re-use it next time
_exportDirectory.set(newDirectory);
}
void DomainBakeWidget::bakeButtonClicked() {
// make sure we have a valid output directory
QDir outputDirectory(_outputDirLineEdit->text());
if (!outputDirectory.exists()) {
return;
}
// make sure we have a non empty URL to an entities file to bake
if (!_entitiesFileLineEdit->text().isEmpty()) {
// everything seems to be in place, kick off a bake for this entities file now
auto fileToBakeURL = QUrl::fromLocalFile(_entitiesFileLineEdit->text());
_baker = std::unique_ptr<DomainBaker> { new DomainBaker(fileToBakeURL, outputDirectory.absolutePath()) };
_baker->start();
return;
}
}
void DomainBakeWidget::cancelButtonClicked() {
// the user wants to go back to the mode selection screen
// remove ourselves from the stacked widget and call delete later so we'll be cleaned up
auto stackedWidget = qobject_cast<QStackedWidget*>(parentWidget());
stackedWidget->removeWidget(this);
this->deleteLater();
}

View file

@ -0,0 +1,49 @@
//
// DomainBakeWidget.h
// tools/oven/src/ui
//
// Created by Stephen Birarda on 4/12/17.
// Copyright 2017 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
//
#ifndef hifi_DomainBakeWidget_h
#define hifi_DomainBakeWidget_h
#include <QtWidgets/QWidget>
#include <SettingHandle.h>
#include "../DomainBaker.h"
class QLineEdit;
class DomainBakeWidget : public QWidget {
Q_OBJECT
public:
DomainBakeWidget(QWidget* parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags());
private slots:
void chooseFileButtonClicked();
void chooseOutputDirButtonClicked();
void bakeButtonClicked();
void cancelButtonClicked();
void outputDirectoryChanged(const QString& newDirectory);
private:
void setupUI();
std::unique_ptr<DomainBaker> _baker;
QLineEdit* _entitiesFileLineEdit;
QLineEdit* _outputDirLineEdit;
Setting::Handle<QString> _exportDirectory;
Setting::Handle<QString> _browseStartDirectory;
};
#endif // hifi_ModelBakeWidget_h

View file

@ -42,8 +42,6 @@ private:
QLineEdit* _modelLineEdit;
QLineEdit* _outputDirLineEdit;
QUrl modelToBakeURL;
Setting::Handle<QString> _exportDirectory;
Setting::Handle<QString> _modelStartDirectory;
};

View file

@ -13,6 +13,7 @@
#include <QtWidgets/QPushButton>
#include <QtWidgets/QStackedWidget>
#include "DomainBakeWidget.h"
#include "ModelBakeWidget.h"
#include "ModesWidget.h"
@ -34,6 +35,7 @@ void ModesWidget::setupUI() {
// add a button for domain baking
QPushButton* domainButton = new QPushButton("Bake Domain");
connect(domainButton, &QPushButton::clicked, this, &ModesWidget::showDomainBakingWidget);
horizontalLayout->addWidget(domainButton);
// add a button for texture baking
@ -49,3 +51,10 @@ void ModesWidget::showModelBakingWidget() {
// add a new widget for making baking to the stack, and switch to it
stackedWidget->setCurrentIndex(stackedWidget->addWidget(new ModelBakeWidget));
}
void ModesWidget::showDomainBakingWidget() {
auto stackedWidget = qobject_cast<QStackedWidget*>(parentWidget());
// add a new widget for making baking to the stack, and switch to it
stackedWidget->setCurrentIndex(stackedWidget->addWidget(new DomainBakeWidget));
}

View file

@ -21,6 +21,7 @@ public:
private slots:
void showModelBakingWidget();
void showDomainBakingWidget();
private:
void setupUI();