Added JavaScript Baking

This commit is contained in:
utkarshgautamnyu 2017-09-22 20:57:18 -07:00
parent 921ebc221e
commit cda5e94b33
4 changed files with 438 additions and 7 deletions

View file

@ -30,6 +30,7 @@
#include <ClientServerUtils.h>
#include <FBXBaker.h>
#include <JSBaker.h>
#include <NodeType.h>
#include <SharedUtil.h>
#include <PathUtils.h>
@ -49,10 +50,11 @@ static const int INTERFACE_RUNNING_CHECK_FREQUENCY_MS = 1000;
const QString ASSET_SERVER_LOGGING_TARGET_NAME = "asset-server";
static const QStringList BAKEABLE_MODEL_EXTENSIONS = { "fbx" };
static const QStringList BAKEABLE_MODEL_EXTENSIONS = { "fbx" , "js" };
static QStringList BAKEABLE_TEXTURE_EXTENSIONS;
static const QString BAKED_MODEL_SIMPLE_NAME = "asset.fbx";
static const QString BAKED_TEXTURE_SIMPLE_NAME = "texture.ktx";
static const QString BAKED_SCRIPT_SIMPLE_NAME = "script.js";
void AssetServer::bakeAsset(const AssetHash& assetHash, const AssetPath& assetPath, const QString& filePath) {
qDebug() << "Starting bake for: " << assetPath << assetHash;
@ -96,7 +98,11 @@ std::pair<BakingStatus, QString> AssetServer::getAssetStatus(const AssetPath& pa
QString bakedFilename;
if (BAKEABLE_MODEL_EXTENSIONS.contains(extension)) {
bakedFilename = BAKED_MODEL_SIMPLE_NAME;
if (extension == "js") {
bakedFilename = BAKED_SCRIPT_SIMPLE_NAME;
} else {
bakedFilename = BAKED_MODEL_SIMPLE_NAME;
}
} else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(extension.toLocal8Bit()) && hasMetaFile(hash)) {
bakedFilename = BAKED_TEXTURE_SIMPLE_NAME;
} else {
@ -183,7 +189,11 @@ bool AssetServer::needsToBeBaked(const AssetPath& path, const AssetHash& assetHa
}
if (BAKEABLE_MODEL_EXTENSIONS.contains(extension)) {
bakedFilename = BAKED_MODEL_SIMPLE_NAME;
if (extension == "js") {
bakedFilename = BAKED_SCRIPT_SIMPLE_NAME;
} else {
bakedFilename = BAKED_MODEL_SIMPLE_NAME;
}
} else if (loaded && BAKEABLE_TEXTURE_EXTENSIONS.contains(extension.toLocal8Bit())) {
bakedFilename = BAKED_TEXTURE_SIMPLE_NAME;
} else {
@ -485,7 +495,11 @@ void AssetServer::handleGetMappingOperation(ReceivedMessage& message, SharedNode
QString bakedRootFile;
if (BAKEABLE_MODEL_EXTENSIONS.contains(assetPathExtension)) {
bakedRootFile = BAKED_MODEL_SIMPLE_NAME;
if (assetPathExtension == "js") {
bakedRootFile = BAKED_SCRIPT_SIMPLE_NAME;
} else {
bakedRootFile = BAKED_MODEL_SIMPLE_NAME;
}
} else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(assetPathExtension.toLocal8Bit())) {
bakedRootFile = BAKED_TEXTURE_SIMPLE_NAME;
}
@ -1141,6 +1155,7 @@ bool AssetServer::renameMapping(AssetPath oldPath, AssetPath newPath) {
static const QString BAKED_ASSET_SIMPLE_FBX_NAME = "asset.fbx";
static const QString BAKED_ASSET_SIMPLE_TEXTURE_NAME = "texture.ktx";
static const QString BAKED_ASSET_SIMPLE_JS_NAME = "script.js";
QString getBakeMapping(const AssetHash& hash, const QString& relativeFilePath) {
return HIDDEN_BAKED_CONTENT_FOLDER + hash + "/" + relativeFilePath;
@ -1203,8 +1218,9 @@ void AssetServer::handleCompletedBake(QString originalAssetHash, QString origina
// setup the mapping for this bake file
auto relativeFilePath = QUrl(filePath).fileName();
qDebug() << "Relative file path is: " << relativeFilePath;
if (relativeFilePath.endsWith(".fbx", Qt::CaseInsensitive)) {
if (relativeFilePath.endsWith(".js", Qt::CaseInsensitive)) {
relativeFilePath = BAKED_ASSET_SIMPLE_JS_NAME;
} else if (relativeFilePath.endsWith(".fbx", Qt::CaseInsensitive)) {
// for an FBX file, we replace the filename with the simple name
// (to handle the case where two mapped assets have the same hash but different names)
relativeFilePath = BAKED_ASSET_SIMPLE_FBX_NAME;
@ -1350,7 +1366,11 @@ bool AssetServer::setBakingEnabled(const AssetPathList& paths, bool enabled) {
QString bakedFilename;
if (BAKEABLE_MODEL_EXTENSIONS.contains(extension)) {
bakedFilename = BAKED_MODEL_SIMPLE_NAME;
if (extension == "js") {
bakedFilename = BAKED_SCRIPT_SIMPLE_NAME;
} else {
bakedFilename = BAKED_MODEL_SIMPLE_NAME;
}
} else if (BAKEABLE_TEXTURE_EXTENSIONS.contains(extension.toLocal8Bit()) && hasMetaFile(hash)) {
bakedFilename = BAKED_TEXTURE_SIMPLE_NAME;
} else {

View file

@ -15,6 +15,7 @@
#include <FBXBaker.h>
#include <PathUtils.h>
#include <JSBaker.h>
BakeAssetTask::BakeAssetTask(const AssetHash& assetHash, const AssetPath& assetPath, const QString& filePath) :
_assetHash(assetHash),
@ -34,6 +35,10 @@ void BakeAssetTask::run() {
_baker = std::unique_ptr<FBXBaker> {
new FBXBaker(QUrl("file:///" + _filePath), fn, PathUtils::generateTemporaryDir())
};
} else if (_assetPath.endsWith(".js", Qt::CaseInsensitive)) {
_baker = std::unique_ptr<JSBaker>{
new JSBaker(QUrl("file:///" + _filePath), PathUtils::generateTemporaryDir())
};
} else {
_baker = std::unique_ptr<TextureBaker> {
new TextureBaker(QUrl("file:///" + _filePath), image::TextureUsage::CUBE_TEXTURE,

View file

@ -0,0 +1,349 @@
#include <QtConcurrent>
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include <QtCore/QEventLoop>
#include <QtCore/QFileInfo>
#include <QtCore/QThread>
#include <PathUtils.h>
#include "JSBaker.h"
#include "Baker.h"
JSBaker::JSBaker(const QUrl& jsURL, const QString& bakedOutputDir) :
_jsURL(jsURL),
_bakedOutputDir(bakedOutputDir)
{
};
void JSBaker::bake() {
auto tempDir = PathUtils::generateTemporaryDir();
if (tempDir.isEmpty()) {
handleError("Failed to create a temporary directory.");
return;
}
_tempDir = tempDir;
_originalJSFilePath = _tempDir.filePath(_jsURL.fileName());
qDebug() << "Made temporary dir " << _tempDir;
qDebug() << "Origin file path: " << _originalJSFilePath;
connect(this, &JSBaker::sourceCopyReadyToLoad, this, &JSBaker::bakeSourceCopy);
loadSourceJS();
}
void JSBaker::loadSourceJS() {
// load the local file
QFile localJS{ _jsURL.toLocalFile() };
if (!localJS.exists()) {
handleError("Could not find " + _jsURL.toString());
return;
}
// copy to original file
qDebug() << "Local file url: " << _jsURL << _jsURL.toString() << _jsURL.toLocalFile() << ", copying to: " << _originalJSFilePath;
localJS.copy(_originalJSFilePath);
// emit signal to indicate script is ready to import
emit sourceCopyReadyToLoad();
}
void JSBaker::bakeSourceCopy() {
importScript();
if (hasErrors()) {
return;
}
/*bakeScript();
if (hasErrors()) {
return;
}*/
/*exportScript();
if (hasErrors()) {
return;
}*/
}
void JSBaker::importScript() {
//qDebug() << "file path: " << _originalJSFilePath.toLocal8Bit().data() << QDir(_originalJSFilePath).exists();
// Import the file to be processed
QFile jsFile(_originalJSFilePath);
if (!jsFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
handleError("Error opening " + _originalJSFilePath + " for reading");
return;
}
// Call Bake Script with the opened file
bakeScript(&jsFile);
}
void JSBaker::bakeScript(QFile* inFile) {
QFile outFile;
outFile.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream in(inFile);
QTextStream out(&outFile);
QChar currentChar;
QChar prevChar;
QChar nextChar;
// Initialize prevChar with new line
prevChar = '\n';
in >> currentChar;
while (!in.atEnd()) {
// Reading per character
in >> nextChar;
if (currentChar == '\r') {
out << '\n';
//currentChar = '\n';
} else if (currentChar == '/') {
if (nextChar == '/') {
handleSingleLineComments(&in);
//out << '\n';
//Start fresh after handling comments
prevChar = '\n';
in >> currentChar;
continue;
} else if (nextChar == '*') {
handleMultiLineComments(&in);
//out << ' ';
//Start fresh after handling comments
prevChar = '\n';
in >> currentChar;
//currentChar = ' ';
continue;
} else {
out << currentChar;
}
} else if (currentChar == ' ' || (int) currentChar.toLatin1() == 9) {
// Handle multiple spaces
if (nextChar == ' ' || (int)currentChar.toLatin1() == 9) {
while (nextChar == ' ' || (int)nextChar.toLatin1() == 9 ) {
in >> nextChar;
if (nextChar == '\n')
break;
}
}
if (!omitSpace(prevChar,nextChar)) {
out << ' ';
}
} else if (currentChar == '\n') {
qDebug() << "prevChar2" << prevChar;
qDebug() << "currentChar2" << currentChar;
qDebug() << "nextChar2" << nextChar;
//Handle multiple new lines
//Hnadle new line followed by space or tab
if (nextChar == '\n' || nextChar == ' ' || (int)nextChar.toLatin1() == 9) {
while (nextChar == '\n' || nextChar == ' ' || (int)nextChar.toLatin1() == 9) {
in >> nextChar;
}
}
if (!omitNewLine(prevChar, nextChar)) {
out << '\n';
}
} else if (currentChar == '"' ) {
//Don't modify quoted strings
out << currentChar;
out << nextChar;
while (nextChar != '"') {
in >> nextChar;
out << nextChar;
}
//Start fresh after handling quoted strings
//prevChar = '"';
prevChar = nextChar;
//currentChar = nextChar;
in >> currentChar;
continue;
} else if (currentChar == "'") {
//Don't modify quoted strings
out << currentChar;
out << nextChar;
while (nextChar != "'") {
in >> nextChar;
out << nextChar;
}
qDebug() << "prevChar" << prevChar;
qDebug() << "currentChar" << currentChar;
qDebug() << "nextChar" << nextChar;
//out << nextChar;
//Start fresh after handling quoted strings
//prevChar = '\'';
prevChar = nextChar;
//currentChar = nextChar;
in >> currentChar;
qDebug() << "prevChar1" << prevChar;
qDebug() << "currentChar1" << currentChar;
qDebug() << "nextChar1" << nextChar;
continue;
} else {
out << currentChar;
}
prevChar = currentChar;
currentChar = nextChar;
}
//Output current character when next character reaches EOF
if (currentChar != '\n') {
out << currentChar;
}
inFile->close();
exportScript(&outFile);
}
void JSBaker::handleSingleLineComments(QTextStream * in) {
QChar ch;
while (!in->atEnd()) {
*in >> ch;
if (ch <= '\n')
break;
}
//*out << '\n';
}
void JSBaker::handleMultiLineComments(QTextStream * in) {
QChar ch;
while (!in->atEnd()) {
*in >> ch;
if (ch == '*') {
if (in->read(1) == '/') {
return;
}
}
}
handleError("Eror unterminated multi line comment");
}
bool JSBaker::isAlphanum(QChar c) {
return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'Z') || c == '_' || c == '$' || c == '\\' ||
c > 126);
}
bool JSBaker::omitSpace(QChar prevChar, QChar nextChar) {
if ((isAlphanum(prevChar) || isNonAscii(prevChar) || isSpecialChar(prevChar)) &&
(isAlphanum(nextChar) || isNonAscii(nextChar) || isSpecialChar(nextChar))
) {
return false;
}
return true;
}
bool JSBaker::isNonAscii(QChar c) {
return ((int)c.toLatin1() > 127);
}
bool JSBaker::isSpecialChar(QChar c) {
if (c == '\'' || c == '$' || c == '_')
return true;
return false;
}
bool JSBaker::isSpecialCharPre(QChar c) {
if (c == '\'' || c == '$' || c == '_' || c == '}' || c == ']' || c == ')' || c == '+' || c == '-'
|| c == '"' || c == "'")
return true;
return false;
}
bool JSBaker::isSpecialCharPost(QChar c) {
if (c == '\'' || c == '$' || c == '_' || c == '{' || c == '[' || c == '(' || c == '+' || c == '-' || c == '/')
return true;
return false;
}
bool JSBaker::omitNewLine(QChar prevChar, QChar nextChar) {
if ((isAlphanum(prevChar) || isNonAscii(prevChar) || isSpecialCharPre(prevChar)) &&
(isAlphanum(nextChar) || isNonAscii(nextChar) || isSpecialCharPost(nextChar))
) {
return false;
}
return true;
}
void JSBaker::exportScript(QFile* bakedFile) {
// save the relative path to this FBX inside our passed output folder
auto fileName = _jsURL.fileName();
auto baseName = fileName.left(fileName.lastIndexOf('.'));
auto bakedFilename = baseName + BAKED_JS_EXTENSION;
_bakedJSFilePath = _bakedOutputDir + "/" + bakedFilename;
bakedFile->setFileName(_bakedJSFilePath);
//QFile bakedFile(_bakedJSFilePath);
if (!bakedFile->open(QIODevice::WriteOnly)) {
handleError("Error opening " + _bakedJSFilePath + " for writing");
return;
}
/*QByteArray x(5, 'a');
bakedFile.copy();*/
_outputFiles.push_back(_bakedJSFilePath);
qCDebug(model_baking) << "Exported" << _jsURL << "with re-written paths to" << _bakedJSFilePath;
bakedFile->close();
emit finished();
}

View file

@ -0,0 +1,57 @@
#ifndef hifi_JSBaker_h
#define hifi_JSBaker_h
#include <QtCore/QFutureSynchronizer>
#include <QtCore/QDir>
#include <QtCore/QUrl>
#include "Baker.h"
#include "TextureBaker.h"
#include "ModelBakingLoggingCategory.h"
static const QString BAKED_JS_EXTENSION = ".baked.js";
class JSBaker : public Baker {
Q_OBJECT
public:
JSBaker(const QUrl& jsURL, const QString& bakedOutputDir);
public slots:
virtual void bake() override;
signals:
void sourceCopyReadyToLoad();
private slots:
void bakeSourceCopy();
private :
QUrl _jsURL;
QString _bakedOutputDir;
QDir _tempDir;
QString _originalJSFilePath;
QString _bakedJSFilePath;
QByteArray _buffer;
void loadSourceJS();
void importScript();
void exportScript(QFile*);
void bakeScript(QFile*);
void handleSingleLineComments(QTextStream*);
void handleMultiLineComments(QTextStream*);
bool isAlphanum(QChar);
bool omitSpace(QChar, QChar);
bool isNonAscii(QChar c);
bool isSpecialChar(QChar c);
bool isSpecialCharPre(QChar c);
bool isSpecialCharPost(QChar c);
bool omitNewLine(QChar, QChar);
};
#endif // !hifi_JSBaker_h