Update baker library with many style improvements

This commit is contained in:
Ryan Huffman 2017-12-08 10:29:26 -08:00
parent 57b943ae98
commit 70c35f84b5
15 changed files with 84 additions and 113 deletions

View file

@ -223,11 +223,13 @@ void FBXBaker::rewriteAndBakeSceneModels() {
auto extractedMesh = FBXReader::extractMesh(objectChild, meshIndex, false);
// Callback to get MaterialID
getMaterialIDCallback materialIDcallback = [=](int partIndex) {return extractedMesh.partMaterialTextures[partIndex].first;};
GetMaterialIDCallback materialIDcallback = [&extractedMesh](int partIndex) {
return extractedMesh.partMaterialTextures[partIndex].first;
};
// Compress mesh information and store in dracoMeshNode
FBXNode dracoMeshNode;
bool success = this->compressMesh(extractedMesh.mesh, hasDeformers, dracoMeshNode, materialIDcallback);
bool success = compressMesh(extractedMesh.mesh, hasDeformers, dracoMeshNode, materialIDcallback);
// if bake fails - return, if there were errors and continue, if there were warnings.
if (!success) {
@ -314,23 +316,20 @@ void FBXBaker::rewriteAndBakeSceneTextures() {
for (FBXNode& textureChild : object->children) {
if (textureChild.name == "RelativeFilename") {
QString fbxTextureFileName { textureChild.properties.at(0).toByteArray() };
QString fbxTextureFileName { textureChild.properties.at(0).toString() };
// Callback to get texture type
getTextureTypeCallback textureTypeCallback = [=]() {
// grab the ID for this texture so we can figure out the
// texture type from the loaded materials
auto textureID{ object->properties[0].toByteArray() };
auto textureType = textureTypes[textureID];
return textureType;
};
// grab the ID for this texture so we can figure out the
// texture type from the loaded materials
auto textureID { object->properties[0].toString() };
auto textureType = textureTypes[textureID];
// Compress the texture information and return the new filename to be added into the FBX scene
QByteArray* bakedTextureFile = this->compressTexture(fbxTextureFileName, textureTypeCallback);
auto bakedTextureFile = compressTexture(fbxTextureFileName, textureType);
// If no errors or warnings have occurred during texture compression add the filename to the FBX scene
if (bakedTextureFile) {
textureChild.properties[0] = *bakedTextureFile;
if (!bakedTextureFile.isNull()) {
textureChild.properties[0] = bakedTextureFile;
} else {
// if bake fails - return, if there were errors and continue, if there were warnings.
if (hasErrors()) {

View file

@ -35,9 +35,6 @@ class FBXBaker : public ModelBaker {
public:
using ModelBaker::ModelBaker;
QUrl getFBXUrl() const { return _modelURL; }
QString getBakedFBXFilePath() const { return _bakedModelFilePath; }
public slots:
virtual void bake() override;

View file

@ -71,7 +71,7 @@ void ModelBaker::abort() {
}
}
bool ModelBaker::compressMesh(FBXMesh& mesh, bool hasDeformers,FBXNode& dracoMeshNode, 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 false;
@ -106,12 +106,12 @@ bool ModelBaker::compressMesh(FBXMesh& mesh, bool hasDeformers,FBXNode& dracoMes
bool hasPerFaceMaterials = (materialIDCallback) ? (mesh.parts.size() > 1 || materialIDCallback(0) != 0 ) : true;
bool needsOriginalIndices{ hasDeformers };
int normalsAttributeID{ -1 };
int colorsAttributeID{ -1 };
int texCoordsAttributeID{ -1 };
int texCoords1AttributeID{ -1 };
int faceMaterialAttributeID{ -1 };
int originalIndexAttributeID{ -1 };
int normalsAttributeID { -1 };
int colorsAttributeID { -1 };
int texCoordsAttributeID { -1 };
int texCoords1AttributeID { -1 };
int faceMaterialAttributeID { -1 };
int originalIndexAttributeID { -1 };
const int positionAttributeID = meshBuilder.AddAttribute(draco::GeometryAttribute::POSITION,
3, draco::DT_FLOAT32);
@ -244,14 +244,7 @@ bool ModelBaker::compressMesh(FBXMesh& mesh, bool hasDeformers,FBXNode& dracoMes
return true;
}
QByteArray* ModelBaker::compressTexture(QString modelTextureFileName, getTextureTypeCallback textureTypeCallback) {
static QByteArray textureChild;
QByteArray textureContent = "";
image::TextureUsage::Type textureType = image::TextureUsage::Type::DEFAULT_TEXTURE;
if (textureTypeCallback) {
textureType = textureTypeCallback();
}
QString ModelBaker::compressTexture(QString modelTextureFileName, image::TextureUsage::Type textureType) {
QFileInfo modelTextureFileInfo{ modelTextureFileName.replace("\\", "/") };
@ -259,18 +252,20 @@ QByteArray* ModelBaker::compressTexture(QString modelTextureFileName, getTexture
// re-baking a model that already references baked textures
// this is an error - return from here
handleError("Cannot re-bake a file that already references compressed textures");
return nullptr;
return QString::null;
}
if (!TextureBaker::getSupportedFormats().contains(modelTextureFileInfo.suffix())) {
// this is a texture format we don't bake, skip it
handleWarning(modelTextureFileName + " is not a bakeable texture format");
return nullptr;
return QString::null;
}
// make sure this texture points to something and isn't one we've already re-mapped
QString textureChild { QString::null };
if (!modelTextureFileInfo.filePath().isEmpty()) {
// check if this was an embedded texture that we already have in-memory content for
QByteArray textureContent;
// figure out the URL to this texture, embedded or external
if (!modelTextureFileInfo.filePath().isEmpty()) {
@ -296,7 +291,7 @@ QByteArray* ModelBaker::compressTexture(QString modelTextureFileName, getTexture
_bakedOutputDir + "/" + bakedTextureFileName
};
textureChild = bakedTextureFileName.toLocal8Bit();
textureChild = bakedTextureFileName;
if (!_bakingTextures.contains(urlToTexture)) {
_outputFiles.push_back(bakedTextureFilePath);
@ -306,11 +301,11 @@ QByteArray* ModelBaker::compressTexture(QString modelTextureFileName, getTexture
}
}
return &textureChild;
return textureChild;
}
void ModelBaker::bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType,
const QDir& outputDir, const QString& bakedFilename, const QByteArray& textureContent) {
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{

View file

@ -27,8 +27,7 @@
#include <FBX.h>
using TextureBakerThreadGetter = std::function<QThread*()>;
using getMaterialIDCallback = std::function <int(int)>;
using getTextureTypeCallback = std::function<image::TextureUsage::Type()>;
using GetMaterialIDCallback = std::function <int(int)>;
class ModelBaker : public Baker {
Q_OBJECT
@ -37,10 +36,13 @@ public:
ModelBaker(const QUrl& inputModelURL, TextureBakerThreadGetter inputTextureThreadGetter,
const QString& bakedOutputDirectory, const QString& originalOutputDirectory = "");
virtual ~ModelBaker();
bool compressMesh(FBXMesh& mesh, bool hasDeformers, FBXNode& dracoMeshNode, getMaterialIDCallback materialIDCallback = NULL);
QByteArray* compressTexture(QString textureFileName, getTextureTypeCallback textureTypeCallback = NULL);
bool compressMesh(FBXMesh& mesh, bool hasDeformers, FBXNode& dracoMeshNode, GetMaterialIDCallback materialIDCallback = nullptr);
QString compressTexture(QString textureFileName, image::TextureUsage::Type = image::TextureUsage::Type::DEFAULT_TEXTURE);
virtual void setWasAborted(bool wasAborted) override;
QUrl getModelURL() const { return _modelURL; }
QString getBakedModelFilePath() const { return _bakedModelFilePath; }
public slots:
virtual void abort() override;

View file

@ -134,7 +134,7 @@ void OBJBaker::bakeOBJ() {
bool combineParts = true; // set true so that OBJReader reads material info from material library
OBJReader reader;
FBXGeometry* geometry = reader.readOBJ(objData, QVariantHash(), combineParts, _modelURL);
auto geometry = reader.readOBJ(objData, QVariantHash(), combineParts, _modelURL);
// Write OBJ Data as FBX tree nodes
FBXNode rootNode;
@ -211,7 +211,7 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
// Compress the mesh information and store in dracoNode
bool hasDeformers = false; // No concept of deformers for an OBJ
FBXNode dracoNode;
this->compressMesh(geometry.meshes[0], hasDeformers, dracoNode);
compressMesh(geometry.meshes[0], hasDeformers, dracoNode);
geometryNode.children.append(dracoNode);
// Generating Object node's child - Model node
@ -219,11 +219,7 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
modelNode.name = MODEL_NODE_NAME;
{
_modelID = _nodeID++;
modelNode.properties = {
_nodeID,
MODEL_NODE_NAME,
MESH
};
modelNode.properties = { _nodeID, MODEL_NODE_NAME, MESH };
}
_objectNode.children = { geometryNode, modelNode };
@ -253,7 +249,7 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
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.emplace_back(_textureID, i);
FBXNode textureNode;
{
@ -277,18 +273,15 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
QByteArray textureFileName = (!currentMaterial.albedoTexture.filename.isEmpty()) ? currentMaterial.albedoTexture.filename : currentMaterial.specularTexture.filename;
// Callback to get Texture content and type
getTextureTypeCallback textureContentTypeCallback = [=]() {
return (!currentMaterial.albedoTexture.filename.isEmpty()) ? image::TextureUsage::Type::ALBEDO_TEXTURE : image::TextureUsage::Type::SPECULAR_TEXTURE;
};
auto textureType = (!currentMaterial.albedoTexture.filename.isEmpty()) ? image::TextureUsage::Type::ALBEDO_TEXTURE : image::TextureUsage::Type::SPECULAR_TEXTURE;
// Compress the texture using ModelBaker::compressTexture() and store compressed file's name in the node
QByteArray* textureFile = this->compressTexture(textureFileName, textureContentTypeCallback);
if (!textureFile) {
auto textureFile = compressTexture(textureFileName, textureType);
if (textureFile.isNull()) {
// Baking failed return
return;
}
relativeFilenameNode.properties = { *textureFile };
relativeFilenameNode.properties = { textureFile };
textureNode.children = { textureNameNode, relativeFilenameNode };
@ -303,27 +296,19 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
// connect Geometry to Model
FBXNode cNode;
cNode.name = C_NODE_NAME;
cNode.properties = {
CONNECTIONS_NODE_PROPERTY,
_geometryID,
_modelID
};
cNode.properties = { CONNECTIONS_NODE_PROPERTY, _geometryID, _modelID };
connectionsNode.children = { cNode };
// connect all materials to model
for (auto& materialID : _materialIDs) {
FBXNode cNode;
cNode.name = C_NODE_NAME;
cNode.properties = {
CONNECTIONS_NODE_PROPERTY,
materialID,
_modelID
};
cNode.properties = { CONNECTIONS_NODE_PROPERTY, materialID, _modelID };
connectionsNode.children.append(cNode);
}
// Connect textures to materials
for (auto& texMat : _mapTextureMaterial) {
for (const auto& texMat : _mapTextureMaterial) {
FBXNode cAmbientNode;
cAmbientNode.name = C_NODE_NAME;
cAmbientNode.properties = {
@ -353,11 +338,7 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
void OBJBaker::setMaterialNodeProperties(FBXNode& materialNode, QString material, FBXGeometry& geometry) {
auto materialID = _nodeID++;
_materialIDs.push_back(materialID);
materialNode.properties = {
materialID,
material,
MESH
};
materialNode.properties = { materialID, material, MESH };
FBXMaterial currentMaterial = geometry.materials[material];

View file

@ -20,6 +20,9 @@
using TextureBakerThreadGetter = std::function<QThread*()>;
using TextureID = int64_t;
using MaterialID = int64_t;
class OBJBaker : public ModelBaker {
Q_OBJECT
public:
@ -43,9 +46,9 @@ private:
qlonglong _nodeID = 0;
qlonglong _geometryID;
qlonglong _modelID;
std::vector<qlonglong> _materialIDs;
std::vector<MaterialID> _materialIDs;
qlonglong _textureID;
std::vector<QPair<qlonglong, int>> _mapTextureMaterial;
std::vector<std::pair<TextureID, int>> _mapTextureMaterial;
FBXNode _objectNode;
};
#endif // hifi_OBJBaker_h

View file

@ -490,13 +490,13 @@ done:
}
FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url) {
FBXGeometry::Pointer OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url) {
PROFILE_RANGE_EX(resource_parse, __FUNCTION__, 0xffff0000, nullptr);
QBuffer buffer { &model };
buffer.open(QIODevice::ReadOnly);
FBXGeometry* geometryPtr = new FBXGeometry();
FBXGeometry& geometry = *geometryPtr;
auto geometryPtr { std::make_shared<FBXGeometry>() };
FBXGeometry& geometry { *geometryPtr };
OBJTokenizer tokenizer { &buffer };
float scaleGuess = 1.0f;

View file

@ -75,7 +75,7 @@ public:
QString currentMaterialName;
QHash<QString, OBJMaterial> materials;
FBXGeometry* readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url = QUrl());
FBXGeometry::Pointer readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url = QUrl());
private:
QUrl _url;

View file

@ -186,11 +186,11 @@ void GeometryReader::run() {
throw QString("empty geometry, possibly due to an unsupported FBX version");
}
} else if (_url.path().toLower().endsWith(".obj")) {
fbxGeometry.reset(OBJReader().readOBJ(_data, _mapping, _combineParts, _url));
fbxGeometry = OBJReader().readOBJ(_data, _mapping, _combineParts, _url);
} else if (_url.path().toLower().endsWith(".obj.gz")) {
QByteArray uncompressedData;
if (gunzip(_data, uncompressedData)){
fbxGeometry.reset(OBJReader().readOBJ(uncompressedData, _mapping, _combineParts, _url));
fbxGeometry = OBJReader().readOBJ(uncompressedData, _mapping, _combineParts, _url);
} else {
throw QString("failed to decompress .obj.gz" );
}

View file

@ -304,11 +304,11 @@ void DomainBaker::handleFinishedModelBaker() {
if (baker) {
if (!baker->hasErrors()) {
// this FBXBaker is done and everything went according to plan
qDebug() << "Re-writing entity references to" << baker->getFBXUrl();
qDebug() << "Re-writing entity references to" << baker->getModelURL();
// enumerate the QJsonRef values for the URL of this FBX from our multi hash of
// entity objects needing a URL re-write
for (QJsonValueRef entityValue : _entitiesNeedingRewrite.values(baker->getFBXUrl())) {
for (QJsonValueRef entityValue : _entitiesNeedingRewrite.values(baker->getModelURL())) {
// convert the entity QJsonValueRef to a QJsonObject so we can modify its URL
auto entity = entityValue.toObject();
@ -317,7 +317,7 @@ void DomainBaker::handleFinishedModelBaker() {
QUrl oldModelURL { entity[ENTITY_MODEL_URL_KEY].toString() };
// setup a new URL using the prefix we were passed
auto relativeFBXFilePath = baker->getBakedFBXFilePath().remove(_contentOutputPath);
auto relativeFBXFilePath = baker->getBakedModelFilePath().remove(_contentOutputPath);
if (relativeFBXFilePath.startsWith("/")) {
relativeFBXFilePath = relativeFBXFilePath.right(relativeFBXFilePath.length() - 1);
}
@ -370,10 +370,10 @@ void DomainBaker::handleFinishedModelBaker() {
}
// remove the baked URL from the multi hash of entities needing a re-write
_entitiesNeedingRewrite.remove(baker->getFBXUrl());
_entitiesNeedingRewrite.remove(baker->getModelURL());
// drop our shared pointer to this baker so that it gets cleaned up
_modelBakers.remove(baker->getFBXUrl());
_modelBakers.remove(baker->getModelURL());
// emit progress to tell listeners how many models we have baked
emit bakeProgress(++_completedSubBakes, _totalNumberOfSubBakes);

View file

@ -42,5 +42,5 @@ void BakeWidget::cancelButtonClicked() {
auto stackedWidget = qobject_cast<QStackedWidget*>(parentWidget());
stackedWidget->removeWidget(this);
this->deleteLater();
deleteLater();
}

View file

@ -204,31 +204,30 @@ void ModelBakeWidget::bakeButtonClicked() {
bakedOutputDirectory.mkdir(".");
originalOutputDirectory.mkdir(".");
std::unique_ptr<Baker> baker;
auto getWorkerThreadCallback = []() -> QThread* {
return qApp->getNextWorkerThread();
};
// everything seems to be in place, kick off a bake for this model now
if (modelToBakeURL.fileName().endsWith(".fbx")) {
_baker = std::unique_ptr<FBXBaker>{
new FBXBaker(modelToBakeURL, []() -> QThread* {
return qApp->getNextWorkerThread();
}, bakedOutputDirectory.absolutePath(), originalOutputDirectory.absolutePath())
};
_isFBX = true;
baker.reset(new FBXBaker(modelToBakeURL, getWorkerThreadCallback, bakedOutputDirectory.absolutePath(),
originalOutputDirectory.absolutePath()));
} else if (modelToBakeURL.fileName().endsWith(".obj")) {
_baker = std::unique_ptr<OBJBaker>{
new OBJBaker(modelToBakeURL, []() -> QThread* {
return qApp->getNextWorkerThread();
}, bakedOutputDirectory.absolutePath(), originalOutputDirectory.absolutePath())
};
_isOBJ = true;
baker.reset(new OBJBaker(modelToBakeURL, getWorkerThreadCallback, bakedOutputDirectory.absolutePath(),
originalOutputDirectory.absolutePath()));
} else {
qWarning() << "Unknown model type: " << modelToBakeURL.fileName());
continue;
}
// move the baker to the FBX/OBJ baker thread
_baker->moveToThread(qApp->getNextWorkerThread());
baker->moveToThread(qApp->getNextWorkerThread());
// invoke the bake method on the baker thread
QMetaObject::invokeMethod(_baker.get(), "bake");
QMetaObject::invokeMethod(baker.get(), "bake");
// make sure we hear about the results of this baker when it is done
connect(_baker.get(), &Baker::finished, this, &ModelBakeWidget::handleFinishedBaker);
connect(baker.get(), &Baker::finished, this, &ModelBakeWidget::handleFinishedBaker);
// add a pending row to the results window to show that this bake is in process
auto resultsWindow = qApp->getMainWindow()->showResultsWindow();
@ -236,16 +235,15 @@ void ModelBakeWidget::bakeButtonClicked() {
// keep a unique_ptr to this baker
// and remember the row that represents it in the results table
_bakers.emplace_back(std::move(_baker), resultsRow);
_bakers.emplace_back(std::move(baker), resultsRow);
}
}
void ModelBakeWidget::handleFinishedBaker() {
Baker* baker;
if (_isFBX) {
baker = qobject_cast<FBXBaker*>(sender());
} else if (_isOBJ) {
baker = qobject_cast<OBJBaker*>(sender());
Baker* baker = dynamic_cast<Baker*>(sender());
if (!baker) {
qWarning() << "Received signal from unexpected sender";
return;
}
// add the results of this bake to the results window

View file

@ -49,9 +49,6 @@ private:
Setting::Handle<QString> _modelStartDirectory;
std::unique_ptr<Baker> _baker;
bool _isOBJ = false;
bool _isFBX = false;
};
#endif // hifi_ModelBakeWidget_h

View file

@ -46,7 +46,7 @@ ResultsWindow* OvenMainWindow::showResultsWindow(bool shouldRaise) {
_resultsWindow->show();
// place the results window initially below our window
_resultsWindow->move(_resultsWindow->x(), this->frameGeometry().bottom());
_resultsWindow->move(_resultsWindow->x(), frameGeometry().bottom());
}
// show the results window and make sure it is in front

View file

@ -41,18 +41,17 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) {
}
try {
QByteArray fbxContents = fbx.readAll();
FBXGeometry* geom;
FBXGeometry::Pointer geom;
if (filename.toLower().endsWith(".obj")) {
bool combineParts = false;
geom = OBJReader().readOBJ(fbxContents, QVariantHash(), combineParts);
} else if (filename.toLower().endsWith(".fbx")) {
geom = readFBX(fbxContents, QVariantHash(), filename);
geom.reset(readFBX(fbxContents, QVariantHash(), filename));
} else {
qWarning() << "file has unknown extension" << filename;
return false;
}
result = *geom;
delete geom;
reSortFBXGeometryMeshes(result);
} catch (const QString& error) {