3
0
Fork 0
mirror of https://github.com/lubosz/overte.git synced 2025-04-12 09:42:28 +02:00

use FBXReader/FBXWriter for texture baking in FBXBaker

This commit is contained in:
Stephen Birarda 2017-09-07 18:38:29 -07:00
parent 7214f57376
commit b153d1e177
8 changed files with 87 additions and 65 deletions

View file

@ -35,9 +35,6 @@
#include "FBXBaker.h"
std::once_flag onceFlag;
FBXSDKManagerUniquePointer FBXBaker::_sdkManager { nullptr };
FBXBaker::FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGetter,
const QString& bakedOutputDir, const QString& originalOutputDir) :
_fbxURL(fbxURL),
@ -45,12 +42,7 @@ FBXBaker::FBXBaker(const QUrl& fbxURL, TextureBakerThreadGetter textureThreadGet
_originalOutputDir(originalOutputDir),
_textureThreadGetter(textureThreadGetter)
{
std::call_once(onceFlag, [](){
// create the static FBX SDK manager
_sdkManager = FBXSDKManagerUniquePointer(FbxManager::Create(), [](FbxManager* manager){
manager->Destroy();
});
});
}
void FBXBaker::bake() {
@ -216,8 +208,12 @@ void FBXBaker::importScene() {
return;
}
qCDebug(model_baking) << "Imported" << _fbxURL << "to FbxScene";
_rootNode = FBXReader::parseFBX(&fbxFile);
FBXReader reader;
qCDebug(model_baking) << "Parsing" << _fbxURL;
_rootNode = reader._rootNode = reader.parseFBX(&fbxFile);
_geometry = *reader.extractFBXGeometry({}, _fbxURL.toString());
_textureContent = reader._textureContent;
}
QString texturePathRelativeToFBX(QUrl fbxURL, QUrl textureURL) {
@ -254,7 +250,7 @@ QString FBXBaker::createBakedTextureFileName(const QFileInfo& textureFileInfo) {
return bakedTextureFileName;
}
QUrl FBXBaker::getTextureURL(const QFileInfo& textureFileInfo, FbxFileTexture* fileTexture) {
QUrl FBXBaker::getTextureURL(const QFileInfo& textureFileInfo, QString relativeFileName) {
QUrl urlToTexture;
if (textureFileInfo.exists() && textureFileInfo.isFile()) {
@ -264,7 +260,6 @@ QUrl FBXBaker::getTextureURL(const QFileInfo& textureFileInfo, FbxFileTexture* f
// external texture that we'll need to download or find
// first check if it the RelativePath to the texture in the FBX was relative
QString relativeFileName = fileTexture->GetRelativeFileName();
auto apparentRelativePath = QFileInfo(relativeFileName.replace("\\", "/"));
// this is a relative file path which will require different handling
@ -336,31 +331,44 @@ image::TextureUsage::Type textureTypeForMaterialProperty(FbxProperty& property,
}
void FBXBaker::rewriteAndBakeSceneTextures() {
using namespace image::TextureUsage;
QHash<QString, image::TextureUsage::Type> textureTypes;
// enumerate the surface materials to find the textures used in the scene
int numMaterials = _scene->GetMaterialCount();
for (int i = 0; i < numMaterials; i++) {
FbxSurfaceMaterial* material = _scene->GetMaterial(i);
// 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;
}
if (material) {
// enumerate the properties of this material to see what texture channels it might have
FbxProperty property = material->GetFirstProperty();
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;
}
while (property.IsValid()) {
// first check if this property has connected textures, if not we don't need to bother with it here
if (property.GetSrcObjectCount<FbxTexture>() > 0) {
// enumerate the children of the root node
for (FBXNode& rootChild : _rootNode.children) {
// figure out the type of texture from the material property
auto textureType = textureTypeForMaterialProperty(property, material);
if (rootChild.name == "Objects") {
if (textureType != image::TextureUsage::UNUSED_TEXTURE) {
int numTextures = property.GetSrcObjectCount<FbxFileTexture>();
// enumerate the objects
auto object = rootChild.children.begin();
while (object != rootChild.children.end()) {
if (object->name == "Texture") {
for (int j = 0; j < numTextures; j++) {
FbxFileTexture* fileTexture = property.GetSrcObject<FbxFileTexture>(j);
// enumerate the texture children
for (FBXNode& textureChild : object->children) {
if (textureChild.name == "RelativeFilename") {
// use QFileInfo to easily split up the existing texture filename into its components
QString fbxTextureFileName { fileTexture->GetFileName() };
QString fbxTextureFileName { textureChild.properties.at(0).toByteArray() };
QFileInfo textureFileInfo { fbxTextureFileName.replace("\\", "/") };
// make sure this texture points to something and isn't one we've already re-mapped
@ -383,38 +391,50 @@ void FBXBaker::rewriteAndBakeSceneTextures() {
};
_outputFiles.push_back(bakedTextureFilePath);
qCDebug(model_baking).noquote() << "Re-mapping" << fileTexture->GetFileName()
<< "to" << bakedTextureFilePath;
qCDebug(model_baking).noquote() << "Re-mapping" << fbxTextureFileName
<< "to" << bakedTextureFileName;
// figure out the URL to this texture, embedded or external
auto urlToTexture = getTextureURL(textureFileInfo, fileTexture);
auto urlToTexture = getTextureURL(textureFileInfo, fbxTextureFileName);
// write the new filename into the FBX scene
fileTexture->SetFileName(bakedTextureFilePath.toUtf8().data());
// write the relative filename to be the baked texture file name since it will
// be right beside the FBX
fileTexture->SetRelativeFileName(bakedTextureFileName.toLocal8Bit().constData());
textureChild.properties[0] = bakedTextureFileName.toLocal8Bit();
if (!_bakingTextures.contains(urlToTexture)) {
// 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];
// check if this was an embedded texture we have already have in-memory content for
auto textureContent = _textureContent.value(fbxTextureFileName.toLocal8Bit());
// bake this texture asynchronously
bakeTexture(urlToTexture, textureType, _bakedOutputDir);
bakeTexture(urlToTexture, textureType, _bakedOutputDir, textureContent);
}
}
}
}
}
property = material->GetNextProperty(property);
++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) {
void FBXBaker::bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType,
const QDir& outputDir, 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),
new TextureBaker(textureURL, textureType, outputDir, textureContent),
&TextureBaker::deleteLater
};
@ -464,7 +484,7 @@ void FBXBaker::handleBakedTexture() {
if (originalTextureFile.open(QIODevice::WriteOnly) && originalTextureFile.write(bakedTexture->getOriginalTexture()) != -1) {
qCDebug(model_baking) << "Saved original texture file" << originalTextureFile.fileName()
<< "for" << _fbxURL;
<< "for" << _fbxURL;
} else {
handleError("Could not save original external texture " + originalTextureFile.fileName()
+ " for " + _fbxURL.toString());

View file

@ -26,15 +26,7 @@
#include <FBX.h>
namespace fbxsdk {
class FbxManager;
class FbxProperty;
class FbxScene;
class FbxFileTexture;
}
static const QString BAKED_FBX_EXTENSION = ".baked.fbx";
using FBXSDKManagerUniquePointer = std::unique_ptr<fbxsdk::FbxManager, std::function<void (fbxsdk::FbxManager *)>>;
using TextureBakerThreadGetter = std::function<QThread*()>;
@ -73,13 +65,16 @@ private:
void checkIfTexturesFinished();
QString createBakedTextureFileName(const QFileInfo& textureFileInfo);
QUrl getTextureURL(const QFileInfo& textureFileInfo, fbxsdk::FbxFileTexture* fileTexture);
QUrl getTextureURL(const QFileInfo& textureFileInfo, QString relativeFileName);
void bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDir);
void bakeTexture(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDir,
const QByteArray& textureContent = QByteArray());
QUrl _fbxURL;
FBXNode _rootNode;
FBXGeometry _geometry;
QHash<QByteArray, QByteArray> _textureContent;
QString _bakedFBXFilePath;
@ -91,9 +86,6 @@ private:
QDir _tempDir;
QString _originalFBXFilePath;
static FBXSDKManagerUniquePointer _sdkManager;
fbxsdk::FbxScene* _scene { nullptr };
QMultiHash<QUrl, QSharedPointer<TextureBaker>> _bakingTextures;
QHash<QString, int> _textureNameMatchCount;

View file

@ -25,8 +25,10 @@
const QString BAKED_TEXTURE_EXT = ".ktx";
TextureBaker::TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDirectory) :
TextureBaker::TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType,
const QDir& outputDirectory, const QByteArray& textureContent) :
_textureURL(textureURL),
_originalTexture(textureContent),
_textureType(textureType),
_outputDirectory(outputDirectory)
{
@ -39,8 +41,13 @@ void TextureBaker::bake() {
// once our texture is loaded, kick off a the processing
connect(this, &TextureBaker::originalTextureLoaded, this, &TextureBaker::processTexture);
// first load the texture (either locally or remotely)
loadTexture();
if (_originalTexture.isEmpty()) {
// first load the texture (either locally or remotely)
loadTexture();
} else {
// we already have a texture passed to us, use that
emit originalTextureLoaded();
}
}
const QStringList TextureBaker::getSupportedFormats() {

View file

@ -27,7 +27,8 @@ class TextureBaker : public Baker {
Q_OBJECT
public:
TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType, const QDir& outputDirectory);
TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType,
const QDir& outputDirectory, const QByteArray& textureContent = QByteArray());
static const QStringList getSupportedFormats();

View file

@ -113,6 +113,7 @@ const int MAX_NUM_PIXELS_FOR_FBX_TEXTURE = 2048 * 2048;
/// A texture map in an FBX document.
class FBXTexture {
public:
QString id;
QString name;
QByteArray filename;
QByteArray content;

View file

@ -939,7 +939,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
QByteArray content;
foreach (const FBXNode& subobject, object.children) {
if (subobject.name == "RelativeFilename") {
filepath= subobject.properties.at(0).toByteArray();
filepath = subobject.properties.at(0).toByteArray();
filepath = filepath.replace('\\', '/');
} else if (subobject.name == "Content" && !subobject.properties.isEmpty()) {

View file

@ -92,6 +92,7 @@ FBXTexture FBXReader::getTexture(const QString& textureID) {
texture.filename = filepath;
}
texture.id = textureID;
texture.name = _textureNames.value(textureID);
texture.transform.setIdentity();
texture.texcoordSet = 0;

View file

@ -119,7 +119,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) {
case QMetaType::QString:
{
auto& bytes = prop.toString().toUtf8();
auto bytes = prop.toString().toUtf8();
out << 'S';
out << bytes.length();
out << bytes;
@ -130,7 +130,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) {
case QMetaType::QByteArray:
{
auto& bytes = prop.toByteArray();
auto bytes = prop.toByteArray();
out.device()->write("S", 1);
out << (int32_t)bytes.size();
out.writeRawData(bytes, bytes.size());
@ -140,7 +140,7 @@ void FBXWriter::encodeFBXProperty(QDataStream& out, const QVariant& prop) {
// TODO Delete? Do we ever use QList instead of QVector?
case QVariant::Type::List:
{
auto& list = prop.toList();
auto list = prop.toList();
auto listType = prop.userType();
switch (listType) {