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:
parent
7214f57376
commit
b153d1e177
8 changed files with 87 additions and 65 deletions
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue