Fixed issues with FBXBaker and refactoring of OBJBaker

This commit is contained in:
utkarshgautamnyu 2017-10-23 20:07:24 -07:00
parent 28baed18c0
commit 46b46c2f4c
7 changed files with 122 additions and 596 deletions

View file

@ -33,19 +33,6 @@
#include "FBXBaker.h"
#ifdef _WIN32
#pragma warning( push )
#pragma warning( disable : 4267 )
#endif
#include <draco/mesh/triangle_soup_mesh_builder.h>
#include <draco/compression/encode.h>
#ifdef _WIN32
#pragma warning( pop )
#endif
FBXBaker::FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGetter,
const QString& bakedOutputDir, const QString& originalOutputDir) :
_fbxURL(fbxURL),
@ -254,72 +241,6 @@ void FBXBaker::importScene() {
_textureContent = reader._textureContent;
}
QString texturePathRelativeToFBX(QUrl fbxURL, QUrl textureURL) {
auto fbxPath = fbxURL.toString(QUrl::RemoveFilename | QUrl::RemoveQuery | QUrl::RemoveFragment);
auto texturePath = textureURL.toString(QUrl::RemoveFilename | QUrl::RemoveQuery | QUrl::RemoveFragment);
if (texturePath.startsWith(fbxPath)) {
// texture path is a child of the FBX path, return the texture path without the fbx path
return texturePath.mid(fbxPath.length());
} else {
// the texture path was not a child of the FBX path, return the empty string
return "";
}
}
QString FBXBaker::createBakedTextureFileName(const QFileInfo& textureFileInfo) {
// first make sure we have a unique base name for this texture
// in case another texture referenced by this model has the same base name
auto& nameMatches = _textureNameMatchCount[textureFileInfo.baseName()];
QString bakedTextureFileName{ textureFileInfo.completeBaseName() };
if (nameMatches > 0) {
// there are already nameMatches texture with this name
// append - and that number to our baked texture file name so that it is unique
bakedTextureFileName += "-" + QString::number(nameMatches);
}
bakedTextureFileName += BAKED_TEXTURE_EXT;
// increment the number of name matches
++nameMatches;
return bakedTextureFileName;
}
QUrl FBXBaker::getTextureURL(const QFileInfo& textureFileInfo, QString relativeFileName, bool isEmbedded) {
QUrl urlToTexture;
auto apparentRelativePath = QFileInfo(relativeFileName.replace("\\", "/"));
if (isEmbedded) {
urlToTexture = _fbxURL.toString() + "/" + apparentRelativePath.filePath();
} else {
if (textureFileInfo.exists() && textureFileInfo.isFile()) {
// set the texture URL to the local texture that we have confirmed exists
urlToTexture = QUrl::fromLocalFile(textureFileInfo.absoluteFilePath());
} else {
// external texture that we'll need to download or find
// this is a relative file path which will require different handling
// depending on the location of the original FBX
if (_fbxURL.isLocalFile() && apparentRelativePath.exists() && apparentRelativePath.isFile()) {
// the absolute path we ran into for the texture in the FBX exists on this machine
// so use that file
urlToTexture = QUrl::fromLocalFile(apparentRelativePath.absoluteFilePath());
} else {
// we didn't find the texture on this machine at the absolute path
// so assume that it is right beside the FBX to match the behaviour of interface
urlToTexture = _fbxURL.resolved(apparentRelativePath.fileName());
}
}
}
return urlToTexture;
}
void FBXBaker::rewriteAndBakeSceneModels() {
unsigned int meshIndex = 0;
bool hasDeformers{ false };
@ -343,17 +264,21 @@ void FBXBaker::rewriteAndBakeSceneModels() {
// TODO Pull this out of _geometry instead so we don't have to reprocess it
auto extractedMesh = FBXReader::extractMesh(objectChild, meshIndex, false);
// Callback to get MaterialID from FBXBaker in ModelBaker
getMaterialIDCallback materialIDcallback = [extractedMesh](int partIndex) {return extractedMesh.partMaterialTextures[partIndex].first;};
getMaterialIDCallback materialIDcallback = [=](int partIndex) {return extractedMesh.partMaterialTextures[partIndex].first;};
// Compress mesh information and store in dracoMeshNode
FBXNode* dracoMeshNode = this->compressMesh(extractedMesh.mesh, hasDeformers, materialIDcallback);
// if dracoMeshNode is null (i.e error occurred while baking), continue iterating through Object node children
if (!dracoMeshNode) {
FBXNode dracoMeshNode;
bool success = this->compressMesh(extractedMesh.mesh, hasDeformers, dracoMeshNode, materialIDcallback);
// if bake fails continue iterating through Object node's children
if (!success) {
continue;
}
objectChild.children.push_back(*dracoMeshNode);
//objectChild.children.push_back(*dracoMeshNode);
objectChild.children.push_back(dracoMeshNode);
static const std::vector<QString> nodeNamesToDelete{
// Node data that is packed into the draco mesh
@ -388,7 +313,6 @@ void FBXBaker::rewriteAndBakeSceneModels() {
}
}
void FBXBaker::rewriteAndBakeSceneTextures() {
using namespace image::TextureUsage;
QHash<QString, image::TextureUsage::Type> textureTypes;
@ -430,99 +354,36 @@ void FBXBaker::rewriteAndBakeSceneTextures() {
for (FBXNode& textureChild : object->children) {
if (textureChild.name == "RelativeFilename") {
// use QFileInfo to easily split up the existing texture filename into its components
QString fbxTextureFileName{ textureChild.properties.at(0).toByteArray() };
// Callback to get texture content and type from FBXBaker in ModelBaker
//auto textureContent = _textureContent.value(fbxTextureFileName.toLocal8Bit());
//qCDebug(model_baking) << "TextureContent" << textureContent.size();
getTextureContentTypeCallback textureContentTypeCallback = [=]() {
QPair<QByteArray, image::TextureUsage::Type> result;
result.first = _textureContent.value(fbxTextureFileName.toLocal8Bit());;
result.second = textureTypes[object->properties[0].toByteArray()];
return result;
};
QByteArray textureContent = "";
QFileInfo fbxTextureFileInfo{ fbxTextureFileName.replace("\\", "/") };
// Callback to get texture content and type from FBXBaker in ModelBaker
if (!fbxTextureFileInfo.filePath().isEmpty()) {
textureContent = _textureContent.value(fbxTextureFileName.toLocal8Bit());
}
// Compress the texture information and return the new filename to be added into the FBX scene
QByteArray* bakedTextureFile = this->compressTexture(fbxTextureFileName, _fbxURL, _bakedOutputDir, _textureThreadGetter, textureContentTypeCallback, _originalOutputDir);
// If no errors or warnings have occurred during texture compression add the filename to the FBX scene
if (bakedTextureFile) {
textureChild.properties[0] = *bakedTextureFile;
} else {
if (hasErrors()) {
return;
} else if (hasWarnings()) {
continue;
}
}
getTextureContentTypeCallback textureContentTypeCallback = [=]() {
QPair<QByteArray, image::TextureUsage::Type> result;
result.first = textureContent;// _textureContent.value(fbxTextureFileName.toLocal8Bit());
auto textureID{ object->properties[0].toByteArray() };
auto textureType = textureTypes[textureID];
result.second = textureType;
return result;
};
// Compress the texture information and return the new filename to be added into the FBX scene
QByteArray* bakedTextureFile = this->compressTexture(fbxTextureFileName, _fbxURL, _bakedOutputDir, _textureThreadGetter, textureContentTypeCallback, _originalOutputDir);
//QFileInfo textureFileInfo{ fbxTextureFileName.replace("\\", "/") };
//if (hasErrors()) {
// return;
//}
//if (textureFileInfo.suffix() == BAKED_TEXTURE_EXT.mid(1)) {
// // re-baking an FBX that already references baked textures is a fail
// // so we add an error and return from here
// handleError("Cannot re-bake a file that references compressed textures");
// return;
//}
//if (!TextureBaker::getSupportedFormats().contains(textureFileInfo.suffix())) {
// // this is a texture format we don't bake, skip it
// handleWarning(fbxTextureFileName + " is not a bakeable texture format");
// continue;
//}
//// make sure this texture points to something and isn't one we've already re-mapped
//if (!textureFileInfo.filePath().isEmpty()) {
// // check if this was an embedded texture we have already have in-memory content for
// auto textureContent = _textureContent.value(fbxTextureFileName.toLocal8Bit());
// // figure out the URL to this texture, embedded or external
// qCDebug(model_baking) << "TextureContent" << textureContent.size();
// auto urlToTexture = getTextureURL(textureFileInfo, fbxTextureFileName,
// !textureContent.isNull());
// QString bakedTextureFileName;
// if (_remappedTexturePaths.contains(urlToTexture)) {
// bakedTextureFileName = _remappedTexturePaths[urlToTexture];
// } else {
// // construct the new baked texture file name and file path
// // ensuring that the baked texture will have a unique name
// // even if there was another texture with the same name at a different path
// bakedTextureFileName = createBakedTextureFileName(textureFileInfo);
// _remappedTexturePaths[urlToTexture] = bakedTextureFileName;
// }
// qCDebug(model_baking).noquote() << "Re-mapping" << fbxTextureFileName
// << "to" << bakedTextureFileName;
// QString bakedTextureFilePath{
// _bakedOutputDir + "/" + bakedTextureFileName
// };
// // write the new filename into the FBX scene
// textureChild.properties[0] = bakedTextureFileName.toLocal8Bit();
// if (!_bakingTextures.contains(urlToTexture)) {
// _outputFiles.push_back(bakedTextureFilePath);
// // grab the ID for this texture so we can figure out the
// // texture type from the loaded materials
// QString textureID{ object->properties[0].toByteArray() };
// auto textureType = textureTypes[textureID];
// // bake this texture asynchronously
// bakeTexture(urlToTexture, textureType, _bakedOutputDir, bakedTextureFileName, textureContent);
// }
//}
// If no errors or warnings have occurred during texture compression add the filename to the FBX scene
if (bakedTextureFile) {
textureChild.properties[0] = *bakedTextureFile;
} else {
if (hasErrors()) {
return;
} else if (hasWarnings()) {
continue;
}
}
}
}
@ -539,203 +400,6 @@ void FBXBaker::rewriteAndBakeSceneTextures() {
}
}
//void FBXBaker::rewriteAndBakeSceneTextures() {
// using namespace image::TextureUsage;
// QHash<QString, image::TextureUsage::Type> textureTypes;
//
// // enumerate the materials in the extracted geometry so we can determine the texture type for each texture ID
// for (const auto& material : _geometry->materials) {
// if (material.normalTexture.isBumpmap) {
// textureTypes[material.normalTexture.id] = BUMP_TEXTURE;
// } else {
// textureTypes[material.normalTexture.id] = NORMAL_TEXTURE;
// }
//
// textureTypes[material.albedoTexture.id] = ALBEDO_TEXTURE;
// textureTypes[material.glossTexture.id] = GLOSS_TEXTURE;
// textureTypes[material.roughnessTexture.id] = ROUGHNESS_TEXTURE;
// textureTypes[material.specularTexture.id] = SPECULAR_TEXTURE;
// textureTypes[material.metallicTexture.id] = METALLIC_TEXTURE;
// textureTypes[material.emissiveTexture.id] = EMISSIVE_TEXTURE;
// textureTypes[material.occlusionTexture.id] = OCCLUSION_TEXTURE;
// textureTypes[material.lightmapTexture.id] = LIGHTMAP_TEXTURE;
// }
//
// // enumerate the children of the root node
// for (FBXNode& rootChild : _rootNode.children) {
//
// if (rootChild.name == "Objects") {
//
// // enumerate the objects
// auto object = rootChild.children.begin();
// while (object != rootChild.children.end()) {
// if (object->name == "Texture") {
//
// // double check that we didn't get an abort while baking the last texture
// if (shouldStop()) {
// return;
// }
//
// // enumerate the texture children
// for (FBXNode& textureChild : object->children) {
//
// if (textureChild.name == "RelativeFilename") {
// QString fbxTextureFileName{ textureChild.properties.at(0).toByteArray() };
//
// // Callback to get texture content and type from FBXBaker in ModelBaker
// auto textureContent = _textureContent.value(fbxTextureFileName.toLocal8Bit());
// qCDebug(model_baking) << "TextureContent" << textureContent;
//
// getTextureContentTypeCallback textureContentTypeCallback = [fbxTextureFileName, textureTypes, object, textureContent]() {
// QPair<QByteArray, image::TextureUsage::Type> result;
// result.first = textureContent;
// result.second = textureTypes[object->properties[0].toByteArray()];
// return result;
// };
//
// // Compress the texture information and return the new filename to be added into the FBX scene
// QByteArray* bakedTextureFile = this->compressTexture(fbxTextureFileName, _fbxURL, _bakedOutputDir, _textureThreadGetter, textureContentTypeCallback, _originalOutputDir);
//
// // If no errors or warnings have occurred during texture compression add the filename to the FBX scene
// if (bakedTextureFile) {
// textureChild.properties[0] = *bakedTextureFile;
// } else {
// if (hasErrors()) {
// return;
// } else if (hasWarnings()) {
// continue;
// }
// }
// }
// }
//
// ++object;
//
// } else if (object->name == "Video") {
// // this is an embedded texture, we need to remove it from the FBX
// object = rootChild.children.erase(object);
// } else {
// ++object;
// }
// }
// }
// }
//}
void FBXBaker::bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType,
const QDir& outputDir, const QString& bakedFilename, const QByteArray& textureContent) {
// start a bake for this texture and add it to our list to keep track of
QSharedPointer<TextureBaker> bakingTexture{
new TextureBaker(textureURL, textureType, outputDir, bakedFilename, textureContent),
&TextureBaker::deleteLater
};
// make sure we hear when the baking texture is done or aborted
connect(bakingTexture.data(), &Baker::finished, this, &FBXBaker::handleBakedTexture);
connect(bakingTexture.data(), &TextureBaker::aborted, this, &FBXBaker::handleAbortedTexture);
// keep a shared pointer to the baking texture
_bakingTextures.insert(textureURL, bakingTexture);
// start baking the texture on one of our available worker threads
bakingTexture->moveToThread(_textureThreadGetter());
QMetaObject::invokeMethod(bakingTexture.data(), "bake");
}
void FBXBaker::handleBakedTexture() {
TextureBaker* bakedTexture = qobject_cast<TextureBaker*>(sender());
// make sure we haven't already run into errors, and that this is a valid texture
if (bakedTexture) {
if (!shouldStop()) {
if (!bakedTexture->hasErrors()) {
if (!_originalOutputDir.isEmpty()) {
// we've been asked to make copies of the originals, so we need to make copies of this if it is a linked texture
// use the path to the texture being baked to determine if this was an embedded or a linked texture
// it is embeddded if the texure being baked was inside a folder with the name of the FBX
// since that is the fake URL we provide when baking external textures
if (!_fbxURL.isParentOf(bakedTexture->getTextureURL())) {
// for linked textures we want to save a copy of original texture beside the original FBX
qCDebug(model_baking) << "Saving original texture for" << bakedTexture->getTextureURL();
// check if we have a relative path to use for the texture
auto relativeTexturePath = texturePathRelativeToFBX(_fbxURL, bakedTexture->getTextureURL());
QFile originalTextureFile{
_originalOutputDir + "/" + relativeTexturePath + bakedTexture->getTextureURL().fileName()
};
if (relativeTexturePath.length() > 0) {
// make the folders needed by the relative path
}
if (originalTextureFile.open(QIODevice::WriteOnly) && originalTextureFile.write(bakedTexture->getOriginalTexture()) != -1) {
qCDebug(model_baking) << "Saved original texture file" << originalTextureFile.fileName()
<< "for" << _fbxURL;
} else {
handleError("Could not save original external texture " + originalTextureFile.fileName()
+ " for " + _fbxURL.toString());
return;
}
}
}
// now that this texture has been baked and handled, we can remove that TextureBaker from our hash
_bakingTextures.remove(bakedTexture->getTextureURL());
checkIfTexturesFinished();
} else {
// there was an error baking this texture - add it to our list of errors
_errorList.append(bakedTexture->getErrors());
// we don't emit finished yet so that the other textures can finish baking first
_pendingErrorEmission = true;
// now that this texture has been baked, even though it failed, we can remove that TextureBaker from our list
_bakingTextures.remove(bakedTexture->getTextureURL());
// abort any other ongoing texture bakes since we know we'll end up failing
for (auto& bakingTexture : _bakingTextures) {
bakingTexture->abort();
}
checkIfTexturesFinished();
}
} else {
// we have errors to attend to, so we don't do extra processing for this texture
// but we do need to remove that TextureBaker from our list
// and then check if we're done with all textures
_bakingTextures.remove(bakedTexture->getTextureURL());
checkIfTexturesFinished();
}
}
}
void FBXBaker::handleAbortedTexture() {
// grab the texture bake that was aborted and remove it from our hash since we don't need to track it anymore
TextureBaker* bakedTexture = qobject_cast<TextureBaker*>(sender());
if (bakedTexture) {
_bakingTextures.remove(bakedTexture->getTextureURL());
}
// since a texture we were baking aborted, our status is also aborted
_shouldAbort.store(true);
// abort any other ongoing texture bakes since we know we'll end up failing
for (auto& bakingTexture : _bakingTextures) {
bakingTexture->abort();
}
checkIfTexturesFinished();
}
void FBXBaker::exportScene() {
// save the relative path to this FBX inside our passed output folder
auto fileName = _fbxURL.fileName();
@ -781,13 +445,3 @@ void FBXBaker::checkIfTexturesFinished() {
}
}
}
void FBXBaker::setWasAborted(bool wasAborted) {
if (wasAborted != _wasAborted.load()) {
Baker::setWasAborted(wasAborted);
if (wasAborted) {
qCDebug(model_baking) << "Aborted baking" << _fbxURL;
}
}
}

View file

@ -40,9 +40,7 @@ public:
QUrl getFBXUrl() const { return _fbxURL; }
QString getBakedFBXFilePath() const { return _bakedFBXFilePath; }
virtual void setWasAborted(bool wasAborted) override;
public slots:
public slots:
virtual void bake() override;
virtual void abort() override;
@ -52,8 +50,6 @@ signals:
private slots:
void bakeSourceCopy();
void handleFBXNetworkReply();
void handleBakedTexture();
void handleAbortedTexture();
private:
void setupOutputFolder();
@ -68,12 +64,6 @@ private:
void checkIfTexturesFinished();
QString createBakedTextureFileName(const QFileInfo& textureFileInfo);
QUrl getTextureURL(const QFileInfo& textureFileInfo, QString relativeFileName, bool isEmbedded = false);
void bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDir,
const QString& bakedFilename, const QByteArray& textureContent = QByteArray());
QUrl _fbxURL;
FBXNode _rootNode;

View file

@ -12,29 +12,11 @@
#include "ModelBaker.h"
#include <image\Image.h>
#include <QtConcurrent>
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include <QtCore/QEventLoop>
#include <QtCore/QFileInfo>
#include <QtCore/QThread>
#include <mutex>
#include <NetworkAccessManager.h>
#include <SharedUtil.h>
#include <PathUtils.h>
#include <FBXReader.h>
#include <FBXWriter.h>
#include "ModelBakingLoggingCategory.h"
#include "TextureBaker.h"
#include "FBXBaker.h"
#ifdef _WIN32
#pragma warning( push )
#pragma warning( disable : 4267 )
@ -51,20 +33,10 @@ ModelBaker::ModelBaker() {}
void ModelBaker::bake() {}
//void ModelBaker::abort() {
// Baker::abort();
//
// // tell our underlying TextureBaker instances to abort
// // the FBXBaker will wait until all are aborted before emitting its own abort signal
// for (auto& textureBaker : _bakingTextures) {
// textureBaker->abort();
// }
//}
FBXNode* ModelBaker::compressMesh(FBXMesh& mesh, bool hasDeformers, getMaterialIDCallback materialIDCallback) {
bool ModelBaker::compressMesh(FBXMesh& mesh, bool hasDeformers,FBXNode& dracoMeshNode, getMaterialIDCallback materialIDCallback) {
if (mesh.wasCompressed) {
handleError("Cannot re-bake a file that contains compressed mesh");
return nullptr;
return false;
}
Q_ASSERT(mesh.normals.size() == 0 || mesh.normals.size() == mesh.vertices.size());
@ -82,7 +54,7 @@ FBXNode* ModelBaker::compressMesh(FBXMesh& mesh, bool hasDeformers, getMaterialI
}
if (numTriangles == 0) {
return nullptr;
return false;
}
draco::TriangleSoupMeshBuilder meshBuilder;
@ -93,7 +65,7 @@ FBXNode* ModelBaker::compressMesh(FBXMesh& mesh, bool hasDeformers, getMaterialI
bool hasColors{ mesh.colors.size() > 0 };
bool hasTexCoords{ mesh.texCoords.size() > 0 };
bool hasTexCoords1{ mesh.texCoords1.size() > 0 };
bool hasPerFaceMaterials;
bool hasPerFaceMaterials;// { mesh.parts.size() > 1 };
if (materialIDCallback) {
if (mesh.parts.size() > 1 || materialIDCallback(0) != 0) {
hasPerFaceMaterials = true;
@ -101,7 +73,6 @@ FBXNode* ModelBaker::compressMesh(FBXMesh& mesh, bool hasDeformers, getMaterialI
} else {
hasPerFaceMaterials = true;
}
bool needsOriginalIndices{ hasDeformers };
int normalsAttributeID{ -1 };
@ -153,7 +124,6 @@ FBXNode* ModelBaker::compressMesh(FBXMesh& mesh, bool hasDeformers, getMaterialI
} else {
materialID = partIndex;
}
auto addFace = [&](QVector<int>& indices, int index, draco::FaceIndex face) {
int32_t idx0 = indices[index];
int32_t idx1 = indices[index + 1];
@ -210,7 +180,7 @@ FBXNode* ModelBaker::compressMesh(FBXMesh& mesh, bool hasDeformers, getMaterialI
if (!dracoMesh) {
handleWarning("Failed to finalize the baking of a draco Geometry node");
return nullptr;
return false;
}
// we need to modify unique attribute IDs for custom attributes
@ -237,12 +207,13 @@ FBXNode* ModelBaker::compressMesh(FBXMesh& mesh, bool hasDeformers, getMaterialI
draco::EncoderBuffer buffer;
encoder.EncodeMeshToBuffer(*dracoMesh, &buffer);
static FBXNode dracoMeshNode;
dracoMeshNode.name = "DracoMesh";
FBXNode dracoNode;
dracoNode.name = "DracoMesh";
auto value = QVariant::fromValue(QByteArray(buffer.data(), (int)buffer.size()));
dracoMeshNode.properties.append(value);
return &dracoMeshNode;
dracoNode.properties.append(value);
dracoMeshNode = dracoNode;
return true;
}
QByteArray* ModelBaker::compressTexture(QString modelTextureFileName, QUrl modelURL, QString bakedOutputDir, TextureBakerThreadGetter textureThreadGetter,
@ -254,12 +225,16 @@ QByteArray* ModelBaker::compressTexture(QString modelTextureFileName, QUrl model
static QByteArray textureChild;
QPair<QByteArray, image::TextureUsage::Type> textureContentType;
QByteArray textureContent;
image::TextureUsage::Type textureType;
// grab the ID for this texture so we can figure out the
// texture type from the loaded materials
textureContentType = textureContentTypeCallback();
if (textureContentTypeCallback) {
textureContentType = textureContentTypeCallback();
textureContent = textureContentType.first;
textureType = textureContentType.second;
}
QByteArray textureContent = textureContentType.first;
image::TextureUsage::Type textureType = textureContentType.second;
QFileInfo modelTextureFileInfo{ modelTextureFileName.replace("\\", "/") };
@ -281,9 +256,8 @@ QByteArray* ModelBaker::compressTexture(QString modelTextureFileName, QUrl model
// check if this was an embedded texture that we already have in-memory content for
// figure out the URL to this texture, embedded or external
//qCDebug(model_baking) << "TextureContent" << !textureContent.isNull();
auto urlToTexture = getTextureURL(modelTextureFileInfo, modelTextureFileName,
true);
qCDebug(model_baking) << "TextureContent" << !textureContent.isNull();
auto urlToTexture = getTextureURL(modelTextureFileInfo, modelTextureFileName, !textureContent.isNull());
QString bakedTextureFileName;
if (_remappedTexturePaths.contains(urlToTexture)) {
@ -310,7 +284,6 @@ QByteArray* ModelBaker::compressTexture(QString modelTextureFileName, QUrl model
_outputFiles.push_back(bakedTextureFilePath);
// bake this texture asynchronously
qCDebug(model_baking) << "URLHere" << urlToTexture;
bakeTexture(urlToTexture, textureType, bakedOutputDir, bakedTextureFileName, textureContent);
}
}

View file

@ -35,14 +35,13 @@ class ModelBaker : public Baker{
public:
ModelBaker();
FBXNode* compressMesh(FBXMesh& mesh, bool hasDeformers, getMaterialIDCallback materialIDCallback = NULL);
bool compressMesh(FBXMesh& mesh, bool hasDeformers, FBXNode& dracoMeshNode, getMaterialIDCallback materialIDCallback = NULL);
QByteArray* compressTexture(QString textureFileName, QUrl modelUrl, QString bakedOutputDir, TextureBakerThreadGetter textureThreadGetter,
getTextureContentTypeCallback textureContentTypeCallback = NULL, const QString& originalOutputDir = "");
virtual void setWasAborted(bool wasAborted) override;
public slots:
virtual void bake() override;
//virtual void abort() override;
private slots:
void handleBakedTexture();

View file

@ -16,6 +16,8 @@
#include "OBJReader.h"
#include "FBXWriter.h"
const double UNIT_SCALE_FACTOR = 100;
OBJBaker::OBJBaker(const QUrl& objURL, TextureBakerThreadGetter textureThreadGetter,
const QString& bakedOutputDir, const QString& originalOutputDir) :
_objURL(objURL),
@ -149,7 +151,7 @@ void OBJBaker::startBake() {
QByteArray objData = objFile.readAll();
bool combineParts = true;
bool combineParts = true; // set true so that OBJReader reads material info from material library
OBJReader reader;
FBXGeometry* geometry = reader.readOBJ(objData, QVariantHash(), combineParts, _objURL);
@ -211,8 +213,9 @@ void OBJBaker::createFBXNodeTree(FBXNode* objRoot, FBXGeometry* geometry) {
// Compress the mesh information and store in dracoNode
bool hasDeformers = false;
ModelBaker modelBaker;
FBXNode* dracoNode = this->compressMesh(geometry->meshes[0], hasDeformers);
geometryNode.children.append(*dracoNode);
FBXNode dracoNode;
this->compressMesh(geometry->meshes[0], hasDeformers, dracoNode);
geometryNode.children.append(dracoNode);
// Generating Object node's child - Model node
FBXNode modelNode;
@ -222,7 +225,7 @@ void OBJBaker::createFBXNodeTree(FBXNode* objRoot, FBXGeometry* geometry) {
// Generating Objects node's child - Material node
QVector<FBXMeshPart> meshParts = geometry->meshes[0].parts;
for (auto p : meshParts) {
for (auto meshPart : meshParts) {
FBXNode materialNode;
materialNode.name = "Material";
if (geometry->materials.size() == 1) {
@ -230,106 +233,52 @@ void OBJBaker::createFBXNodeTree(FBXNode* objRoot, FBXGeometry* geometry) {
setMaterialNodeProperties(&materialNode, materialID, geometry);
}
} else {
setMaterialNodeProperties(&materialNode, p.materialID, geometry);
setMaterialNodeProperties(&materialNode, meshPart.materialID, geometry);
}
_objectNode.children.append(materialNode);
}
// Texture Node
// Generating Texture Node
int count = 0;
// iterate through mesh parts and process the associated textures
for (int i = 0;i < meshParts.size();i++) {
QString material = meshParts[i].materialID;
FBXMaterial currentMaterial = geometry->materials[material];
if (!currentMaterial.albedoTexture.filename.isEmpty() || !currentMaterial.specularTexture.filename.isEmpty()) {
_textureID = _nodeID;
mapTextureMaterial.push_back(QPair<qlonglong, int>(_textureID, i));
_mapTextureMaterial.push_back(QPair<qlonglong, int>(_textureID, i));
QVariant property0(_nodeID++);
FBXNode textureNode;
textureNode.name = "Texture";
textureNode.properties = { property0 };
// Texture node child - TextureName node
FBXNode textureNameNode;
textureNameNode.name = "TextureName";
QByteArray propertyString = (!currentMaterial.albedoTexture.filename.isEmpty()) ? "Kd" : "Ka";
auto prop0 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
textureNameNode.properties = { prop0 };
// Texture node child - Relative Filename node
FBXNode relativeFilenameNode;
relativeFilenameNode.name = "RelativeFilename";
QByteArray textureFileName = (!currentMaterial.albedoTexture.filename.isEmpty()) ? currentMaterial.albedoTexture.filename : currentMaterial.specularTexture.filename;
// Callback to get Texture content and type
getTextureContentTypeCallback textureContentTypeCallback = [=]() {
QPair<QByteArray, image::TextureUsage::Type> result;
result.first = NULL;
result.second = (!currentMaterial.albedoTexture.filename.isEmpty()) ? image::TextureUsage::Type::ALBEDO_TEXTURE : image::TextureUsage::Type::SPECULAR_TEXTURE;
return result;
};
// Compress the texture using ModelBaker::compressTexture() and store compressed file's name in the node
QByteArray* textureFile = this->compressTexture(textureFileName, _objURL, _bakedOutputDir, _textureThreadGetter, textureContentTypeCallback,_originalOutputDir);
QVariant textureProperty0;
textureProperty0 = QVariant::fromValue(QByteArray(textureFile->data(), (int)textureFile->size()));
relativeFilenameNode.properties = { textureProperty0 };
FBXNode properties70Node;
properties70Node.name = "Properties70";
QVariant texProperty0;
QVariant texProperty1;
QVariant texProperty2;
QVariant texProperty3;
QVariant texProperty4;
double value;
// Set UseMaterial
FBXNode pUseMaterial;
pUseMaterial.name = "P";
propertyString = "UseMaterial";
texProperty0 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "bool";
texProperty1 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "";
texProperty2 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "";
texProperty3 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
int texVal = 1;
texProperty4 = texVal;
pUseMaterial.properties = { texProperty0, texProperty1, texProperty2, texProperty3, texProperty4 };
FBXNode pUVSet;
pUVSet.name = "P";
propertyString = "UVSet";
texProperty0 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "KString";
texProperty1 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "";
texProperty2 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "";
texProperty3 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "";
texProperty4 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
pUVSet.properties = { texProperty0, texProperty1, texProperty2, texProperty3, texProperty4 };
FBXNode pUseMipMap;
pUseMipMap.name = "P";
propertyString = "UseMipMap";
texProperty0 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "bool";
texProperty1 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "";
texProperty2 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "";
texProperty3 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
texVal = 1;
texProperty4 = texVal;
pUseMipMap.properties = { texProperty0, texProperty1, texProperty2, texProperty3, texProperty4 };
properties70Node.children = { pUVSet, pUseMaterial, pUseMipMap };
textureNode.children = { textureNameNode,relativeFilenameNode, properties70Node };
textureNode.children = { textureNameNode,relativeFilenameNode };
_objectNode.children.append(textureNode);
}
@ -338,7 +287,7 @@ void OBJBaker::createFBXNodeTree(FBXNode* objRoot, FBXGeometry* geometry) {
// Generating Connections node
FBXNode connectionsNode;
connectionsNode.name = "Connections";
// connect Geometry -> Model
// connect Geometry to Model
FBXNode cNode1;
cNode1.name = "C";
QByteArray propertyString("OO");
@ -350,7 +299,7 @@ void OBJBaker::createFBXNodeTree(FBXNode* objRoot, FBXGeometry* geometry) {
cNode1.properties = { property0, property1, property2 };
connectionsNode.children = { cNode1};
// connect materials to model
// connect all materials to model
for (int i = 0;i < geometry->materials.size();i++) {
FBXNode cNode;
cNode.name = "C";
@ -361,14 +310,14 @@ void OBJBaker::createFBXNodeTree(FBXNode* objRoot, FBXGeometry* geometry) {
connectionsNode.children.append(cNode);
}
// Texture to material
for (int i = 0;i < mapTextureMaterial.size();i++) {
// Connect textures to materials
for (int i = 0;i < _mapTextureMaterial.size();i++) {
FBXNode cNode2;
cNode2.name = "C";
QByteArray propertyString1("OP");
property0 = QVariant::fromValue(QByteArray(propertyString1.data(), (int)propertyString1.size()));
property1 = mapTextureMaterial[i].first;
int matID = mapTextureMaterial[i].second;
property1 = _mapTextureMaterial[i].first;
int matID = _mapTextureMaterial[i].second;
property2 = _materialIDs[matID];
propertyString1 = "AmbientFactor";
QVariant connectionProperty = QVariant::fromValue(QByteArray(propertyString1.data(), (int)propertyString1.size()));
@ -379,7 +328,7 @@ void OBJBaker::createFBXNodeTree(FBXNode* objRoot, FBXGeometry* geometry) {
cNode4.name = "C";
propertyString1 = "OP";
property0 = QVariant::fromValue(QByteArray(propertyString1.data(), (int)propertyString1.size()));
property1 = mapTextureMaterial[i].first;
property1 = _mapTextureMaterial[i].first;
property2 = _materialIDs[matID];
propertyString1 = "DiffuseColor";
connectionProperty = QVariant::fromValue(QByteArray(propertyString1.data(), (int)propertyString1.size()));
@ -388,24 +337,16 @@ void OBJBaker::createFBXNodeTree(FBXNode* objRoot, FBXGeometry* geometry) {
}
// Connect all generated nodes to rootNode
// Make all generated nodes children of rootNode
objRoot->children = { globalSettingsNode, _objectNode, connectionsNode };
}
void OBJBaker::setProperties(FBXNode* parentNode) {
if (parentNode->name == "P") {
QByteArray propertyString("UnitScaleFactor");
QVariant property0 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "double";
QVariant property1 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "Number";
QVariant property2 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "";
QVariant property3 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
double unitScaleFactor = 100;
QVariant property4(unitScaleFactor);
std::vector<QByteArray> stringProperties{ "UnitScaleFactor", "double", "Number", "" };
std::vector<double> numericProperties{ UNIT_SCALE_FACTOR };
parentNode->properties = { property0, property1, property2, property3, property4 };
setPropertiesList(stringProperties, numericProperties, parentNode->properties);
} else if (parentNode->name == "Geometry") {
_geometryID = _nodeID;
QVariant property0(_nodeID++);
@ -443,91 +384,58 @@ void OBJBaker::setMaterialNodeProperties(FBXNode* materialNode, QString material
FBXNode properties70Node;
properties70Node.name = "Properties70";
QVariant materialProperty0;
QVariant materialProperty1;
QVariant materialProperty2;
QVariant materialProperty3;
QVariant materialProperty4;
QVariant materialProperty5;
QVariant materialProperty6;
double value;
// Set diffuseColor
FBXNode pNodeDiffuseColor;
pNodeDiffuseColor.name = "P";
propertyString = "DiffuseColor";
materialProperty0 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "Color";
materialProperty1 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "";
materialProperty2 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "A";
materialProperty3 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
value = (double)currentMaterial.diffuseColor[0];
materialProperty4 = value;
value = (double)currentMaterial.diffuseColor[1];
materialProperty5 = value;
value = (double)currentMaterial.diffuseColor[2];
materialProperty6 = value;
pNodeDiffuseColor.properties = { materialProperty0, materialProperty1, materialProperty2, materialProperty3, materialProperty4, materialProperty5, materialProperty6 };
std::vector<QByteArray> stringProperties{ "DiffuseColor", "Color", "", "A" };
std::vector<double> numericProperties{ currentMaterial.diffuseColor[0], currentMaterial.diffuseColor[1], currentMaterial.diffuseColor[2] };
setPropertiesList(stringProperties, numericProperties, pNodeDiffuseColor.properties);
properties70Node.children.append(pNodeDiffuseColor);
// Set specularColor
FBXNode pNodeSpecularColor;
pNodeSpecularColor.name = "P";
propertyString = "SpecularColor";
materialProperty0 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "Color";
materialProperty1 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "";
materialProperty2 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "A";
materialProperty3 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
value = (double)currentMaterial.specularColor[0];
materialProperty4 = value;
value = (double)currentMaterial.specularColor[1];
materialProperty5 = value;
value = (double)currentMaterial.specularColor[2];
materialProperty6 = value;
pNodeSpecularColor.properties = { materialProperty0, materialProperty1, materialProperty2, materialProperty3, materialProperty4, materialProperty5, materialProperty6 };
stringProperties = { "SpecularColor", "Color", "", "A" };
numericProperties = { currentMaterial.specularColor[0], currentMaterial.specularColor[1], currentMaterial.specularColor[2] };
setPropertiesList(stringProperties, numericProperties, pNodeSpecularColor.properties);
properties70Node.children.append(pNodeSpecularColor);
// Set Shininess
FBXNode pNodeShininess;
pNodeShininess.name = "P";
propertyString = "Shininess";
materialProperty0 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "Number";
materialProperty1 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "";
materialProperty2 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "A";
materialProperty3 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
value = (double)currentMaterial.shininess;
materialProperty4 = value;
pNodeShininess.properties = { materialProperty0, materialProperty1, materialProperty2, materialProperty3, materialProperty4 };
stringProperties = { "Shininess", "Number", "", "A" };
numericProperties = { currentMaterial.shininess };
setPropertiesList(stringProperties, numericProperties, pNodeShininess.properties);
properties70Node.children.append(pNodeShininess);
// Set Opacity
FBXNode pNodeOpacity;
pNodeOpacity.name = "P";
propertyString = "Opacity";
materialProperty0 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "Number";
materialProperty1 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "";
materialProperty2 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
propertyString = "A";
materialProperty3 = QVariant::fromValue(QByteArray(propertyString.data(), (int)propertyString.size()));
value = (double)currentMaterial.opacity;
materialProperty4 = value;
pNodeOpacity.properties = { materialProperty0, materialProperty1, materialProperty2, materialProperty3, materialProperty4 };
stringProperties = { "Opacity", "Number", "", "A" };
numericProperties = { currentMaterial.opacity };
setPropertiesList(stringProperties, numericProperties, pNodeOpacity.properties);
properties70Node.children.append(pNodeOpacity);
materialNode->children.append(properties70Node);
}
template<typename NumberType>
void OBJBaker::setPropertiesList(std::vector<QByteArray> stringProperties, std::vector<NumberType> numericProperties, QVariantList& propertiesList) {
foreach(auto stringProperty, stringProperties) {
auto propertyValue = QVariant::fromValue(QByteArray(stringProperty.data(), (int)stringProperty.size()));
propertiesList.append(propertyValue);
}
foreach(auto numberProperty, numericProperties) {
QVariant propertyValue(numberProperty);
propertiesList.append(propertyValue);
}
}

View file

@ -31,7 +31,9 @@ public:
void createFBXNodeTree(FBXNode* objRoot, FBXGeometry* geometry);
void setProperties(FBXNode * parentNode);
void setMaterialNodeProperties(FBXNode* materialNode, QString material, FBXGeometry* geometry);
template<typename NumberType>
void setPropertiesList(std::vector<QByteArray> stringProperties, std::vector<NumberType> numericProperties, QVariantList& propertiesList);
public slots:
virtual void bake() override;
@ -57,7 +59,7 @@ private:
qlonglong _modelID;
std::vector<qlonglong> _materialIDs;
qlonglong _textureID;
std::vector<QPair<qlonglong, int>> mapTextureMaterial;
std::vector<QPair<qlonglong, int>> _mapTextureMaterial;
FBXNode _objectNode;
};
#endif // hifi_OBJBaker_h

View file

@ -269,4 +269,4 @@ void ModelBakeWidget::handleFinishedBaker() {
_bakers.erase(it);
}
}
}