mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 04:44:11 +02:00
Added ability to read FBX textures embedded in FBX files.
This commit is contained in:
parent
6251b6b819
commit
197127fbde
6 changed files with 112 additions and 47 deletions
|
@ -306,14 +306,14 @@ bool ModelUploader::addTextures(const QString& texdir, const QString fbxFile) {
|
|||
|
||||
foreach (FBXMesh mesh, geometry.meshes) {
|
||||
foreach (FBXMeshPart part, mesh.parts) {
|
||||
if (!part.diffuseFilename.isEmpty()) {
|
||||
if (!addPart(texdir + "/" + part.diffuseFilename,
|
||||
if (!part.diffuseTexture.filename.isEmpty()) {
|
||||
if (!addPart(texdir + "/" + part.diffuseTexture.filename,
|
||||
QString("texture%1").arg(++_texturesCount))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!part.normalFilename.isEmpty()) {
|
||||
if (!addPart(texdir + "/" + part.normalFilename,
|
||||
if (!part.normalTexture.filename.isEmpty()) {
|
||||
if (!addPart(texdir + "/" + part.normalTexture.filename,
|
||||
QString("texture%1").arg(++_texturesCount))) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -543,14 +543,14 @@ void NetworkGeometry::setGeometry(const FBXGeometry& geometry) {
|
|||
int totalIndices = 0;
|
||||
foreach (const FBXMeshPart& part, mesh.parts) {
|
||||
NetworkMeshPart networkPart;
|
||||
if (!part.diffuseFilename.isEmpty()) {
|
||||
if (!part.diffuseTexture.filename.isEmpty()) {
|
||||
networkPart.diffuseTexture = Application::getInstance()->getTextureCache()->getTexture(
|
||||
_textureBase.resolved(QUrl(part.diffuseFilename)), false, mesh.isEye);
|
||||
_textureBase.resolved(QUrl(part.diffuseTexture.filename)), false, mesh.isEye, part.diffuseTexture.content);
|
||||
networkPart.diffuseTexture->setLoadPriorities(_loadPriorities);
|
||||
}
|
||||
if (!part.normalFilename.isEmpty()) {
|
||||
if (!part.normalTexture.filename.isEmpty()) {
|
||||
networkPart.normalTexture = Application::getInstance()->getTextureCache()->getTexture(
|
||||
_textureBase.resolved(QUrl(part.normalFilename)), true);
|
||||
_textureBase.resolved(QUrl(part.normalTexture.filename)), true, false, part.normalTexture.content);
|
||||
networkPart.normalTexture->setLoadPriorities(_loadPriorities);
|
||||
}
|
||||
networkMesh.parts.append(networkPart);
|
||||
|
|
|
@ -105,13 +105,22 @@ GLuint TextureCache::getBlueTextureID() {
|
|||
return _blueTextureID;
|
||||
}
|
||||
|
||||
QSharedPointer<NetworkTexture> TextureCache::getTexture(const QUrl& url, bool normalMap, bool dilatable) {
|
||||
/// Extra data for creating textures.
|
||||
class TextureExtra {
|
||||
public:
|
||||
bool normalMap;
|
||||
const QByteArray& content;
|
||||
};
|
||||
|
||||
QSharedPointer<NetworkTexture> TextureCache::getTexture(const QUrl& url, bool normalMap,
|
||||
bool dilatable, const QByteArray& content) {
|
||||
if (!dilatable) {
|
||||
return ResourceCache::getResource(url, QUrl(), false, &normalMap).staticCast<NetworkTexture>();
|
||||
TextureExtra extra = { normalMap, content };
|
||||
return ResourceCache::getResource(url, QUrl(), false, &extra).staticCast<NetworkTexture>();
|
||||
}
|
||||
QSharedPointer<NetworkTexture> texture = _dilatableNetworkTextures.value(url);
|
||||
if (texture.isNull()) {
|
||||
texture = QSharedPointer<NetworkTexture>(new DilatableNetworkTexture(url), &Resource::allReferencesCleared);
|
||||
texture = QSharedPointer<NetworkTexture>(new DilatableNetworkTexture(url, content), &Resource::allReferencesCleared);
|
||||
texture->setSelf(texture);
|
||||
texture->setCache(this);
|
||||
_dilatableNetworkTextures.insert(url, texture);
|
||||
|
@ -215,7 +224,9 @@ bool TextureCache::eventFilter(QObject* watched, QEvent* event) {
|
|||
|
||||
QSharedPointer<Resource> TextureCache::createResource(const QUrl& url,
|
||||
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
|
||||
return QSharedPointer<Resource>(new NetworkTexture(url, *(const bool*)extra), &Resource::allReferencesCleared);
|
||||
const TextureExtra* textureExtra = static_cast<const TextureExtra*>(extra);
|
||||
return QSharedPointer<Resource>(new NetworkTexture(url, textureExtra->normalMap, textureExtra->content),
|
||||
&Resource::allReferencesCleared);
|
||||
}
|
||||
|
||||
QOpenGLFramebufferObject* TextureCache::createFramebufferObject() {
|
||||
|
@ -238,8 +249,8 @@ Texture::~Texture() {
|
|||
glDeleteTextures(1, &_id);
|
||||
}
|
||||
|
||||
NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap) :
|
||||
Resource(url),
|
||||
NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap, const QByteArray& content) :
|
||||
Resource(url, !content.isEmpty()),
|
||||
_translucent(false) {
|
||||
|
||||
if (!url.isValid()) {
|
||||
|
@ -250,12 +261,19 @@ NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap) :
|
|||
glBindTexture(GL_TEXTURE_2D, getID());
|
||||
loadSingleColorTexture(normalMap ? OPAQUE_BLUE : OPAQUE_WHITE);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
// if we have content, load it after we have our self pointer
|
||||
if (!content.isEmpty()) {
|
||||
_startedLoading = true;
|
||||
QMetaObject::invokeMethod(this, "loadContent", Qt::QueuedConnection, Q_ARG(const QByteArray&, content));
|
||||
}
|
||||
}
|
||||
|
||||
class ImageReader : public QRunnable {
|
||||
public:
|
||||
|
||||
ImageReader(const QWeakPointer<Resource>& texture, QNetworkReply* reply);
|
||||
ImageReader(const QWeakPointer<Resource>& texture, QNetworkReply* reply, const QUrl& url = QUrl(),
|
||||
const QByteArray& content = QByteArray());
|
||||
|
||||
virtual void run();
|
||||
|
||||
|
@ -263,27 +281,37 @@ private:
|
|||
|
||||
QWeakPointer<Resource> _texture;
|
||||
QNetworkReply* _reply;
|
||||
QUrl _url;
|
||||
QByteArray _content;
|
||||
};
|
||||
|
||||
ImageReader::ImageReader(const QWeakPointer<Resource>& texture, QNetworkReply* reply) :
|
||||
ImageReader::ImageReader(const QWeakPointer<Resource>& texture, QNetworkReply* reply,
|
||||
const QUrl& url, const QByteArray& content) :
|
||||
_texture(texture),
|
||||
_reply(reply) {
|
||||
_reply(reply),
|
||||
_url(url),
|
||||
_content(content) {
|
||||
}
|
||||
|
||||
void ImageReader::run() {
|
||||
QSharedPointer<Resource> texture = _texture.toStrongRef();
|
||||
if (texture.isNull()) {
|
||||
_reply->deleteLater();
|
||||
if (_reply) {
|
||||
_reply->deleteLater();
|
||||
}
|
||||
return;
|
||||
}
|
||||
QUrl url = _reply->url();
|
||||
QImage image = QImage::fromData(_reply->readAll());
|
||||
_reply->deleteLater();
|
||||
if (_reply) {
|
||||
_url = _reply->url();
|
||||
_content = _reply->readAll();
|
||||
_reply->deleteLater();
|
||||
}
|
||||
QImage image = QImage::fromData(_content);
|
||||
|
||||
// enforce a fixed maximum
|
||||
const int MAXIMUM_SIZE = 1024;
|
||||
if (image.width() > MAXIMUM_SIZE || image.height() > MAXIMUM_SIZE) {
|
||||
qDebug() << "Image greater than maximum size:" << url << image.width() << image.height();
|
||||
qDebug() << "Image greater than maximum size:" << _url << image.width() << image.height();
|
||||
image = image.scaled(MAXIMUM_SIZE, MAXIMUM_SIZE, Qt::KeepAspectRatio);
|
||||
}
|
||||
|
||||
|
@ -315,7 +343,7 @@ void ImageReader::run() {
|
|||
}
|
||||
int imageArea = image.width() * image.height();
|
||||
if (opaquePixels == imageArea) {
|
||||
qDebug() << "Image with alpha channel is completely opaque:" << url;
|
||||
qDebug() << "Image with alpha channel is completely opaque:" << _url;
|
||||
image = image.convertToFormat(QImage::Format_RGB888);
|
||||
}
|
||||
QMetaObject::invokeMethod(texture.data(), "setImage", Q_ARG(const QImage&, image),
|
||||
|
@ -327,6 +355,10 @@ void NetworkTexture::downloadFinished(QNetworkReply* reply) {
|
|||
QThreadPool::globalInstance()->start(new ImageReader(_self, reply));
|
||||
}
|
||||
|
||||
void NetworkTexture::loadContent(const QByteArray& content) {
|
||||
QThreadPool::globalInstance()->start(new ImageReader(_self, NULL, _url, content));
|
||||
}
|
||||
|
||||
void NetworkTexture::setImage(const QImage& image, bool translucent) {
|
||||
_translucent = translucent;
|
||||
|
||||
|
@ -348,8 +380,8 @@ void NetworkTexture::imageLoaded(const QImage& image) {
|
|||
// nothing by default
|
||||
}
|
||||
|
||||
DilatableNetworkTexture::DilatableNetworkTexture(const QUrl& url) :
|
||||
NetworkTexture(url, false),
|
||||
DilatableNetworkTexture::DilatableNetworkTexture(const QUrl& url, const QByteArray& content) :
|
||||
NetworkTexture(url, false, content),
|
||||
_innerRadius(0),
|
||||
_outerRadius(0)
|
||||
{
|
||||
|
|
|
@ -44,7 +44,8 @@ public:
|
|||
GLuint getBlueTextureID();
|
||||
|
||||
/// Loads a texture from the specified URL.
|
||||
QSharedPointer<NetworkTexture> getTexture(const QUrl& url, bool normalMap = false, bool dilatable = false);
|
||||
QSharedPointer<NetworkTexture> getTexture(const QUrl& url, bool normalMap = false, bool dilatable = false,
|
||||
const QByteArray& content = QByteArray());
|
||||
|
||||
/// Returns a pointer to the primary framebuffer object. This render target includes a depth component, and is
|
||||
/// used for scene rendering.
|
||||
|
@ -115,7 +116,7 @@ class NetworkTexture : public Resource, public Texture {
|
|||
|
||||
public:
|
||||
|
||||
NetworkTexture(const QUrl& url, bool normalMap);
|
||||
NetworkTexture(const QUrl& url, bool normalMap, const QByteArray& content);
|
||||
|
||||
/// Checks whether it "looks like" this texture is translucent
|
||||
/// (majority of pixels neither fully opaque or fully transparent).
|
||||
|
@ -124,10 +125,12 @@ public:
|
|||
protected:
|
||||
|
||||
virtual void downloadFinished(QNetworkReply* reply);
|
||||
virtual void imageLoaded(const QImage& image);
|
||||
|
||||
|
||||
Q_INVOKABLE void loadContent(const QByteArray& content);
|
||||
Q_INVOKABLE void setImage(const QImage& image, bool translucent);
|
||||
|
||||
virtual void imageLoaded(const QImage& image);
|
||||
|
||||
private:
|
||||
|
||||
bool _translucent;
|
||||
|
@ -139,7 +142,7 @@ class DilatableNetworkTexture : public NetworkTexture {
|
|||
|
||||
public:
|
||||
|
||||
DilatableNetworkTexture(const QUrl& url);
|
||||
DilatableNetworkTexture(const QUrl& url, const QByteArray& content);
|
||||
|
||||
/// Returns a pointer to a texture with the requested amount of dilation.
|
||||
QSharedPointer<Texture> getDilatedTexture(float dilation);
|
||||
|
|
|
@ -935,6 +935,14 @@ public:
|
|||
QVector<float> values;
|
||||
};
|
||||
|
||||
FBXTexture getTexture(const QString& textureID, const QHash<QString, QByteArray>& textureFilenames,
|
||||
const QHash<QByteArray, QByteArray>& textureContent) {
|
||||
FBXTexture texture;
|
||||
texture.filename = textureFilenames.value(textureID);
|
||||
texture.content = textureContent.value(texture.filename);
|
||||
return texture;
|
||||
}
|
||||
|
||||
FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) {
|
||||
QHash<QString, ExtractedMesh> meshes;
|
||||
QVector<ExtractedBlendshape> blendshapes;
|
||||
|
@ -944,6 +952,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
QHash<QString, Cluster> clusters;
|
||||
QHash<QString, AnimationCurve> animationCurves;
|
||||
QHash<QString, QByteArray> textureFilenames;
|
||||
QHash<QByteArray, QByteArray> textureContent;
|
||||
QHash<QString, Material> materials;
|
||||
QHash<QString, QString> diffuseTextures;
|
||||
QHash<QString, QString> bumpTextures;
|
||||
|
@ -952,8 +961,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
QHash<QString, QString> yComponents;
|
||||
QHash<QString, QString> zComponents;
|
||||
|
||||
printNode(node, 0);
|
||||
|
||||
QVariantHash joints = mapping.value("joint").toHash();
|
||||
QString jointEyeLeftName = processID(getString(joints.value("jointEyeLeft", "jointEyeLeft")));
|
||||
QString jointEyeRightName = processID(getString(joints.value("jointEyeRight", "jointEyeRight")));
|
||||
|
@ -1182,6 +1189,21 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
textureFilenames.insert(getID(object.properties), filename);
|
||||
}
|
||||
}
|
||||
} else if (object.name == "Video") {
|
||||
QByteArray filename;
|
||||
QByteArray content;
|
||||
foreach (const FBXNode& subobject, object.children) {
|
||||
if (subobject.name == "RelativeFilename") {
|
||||
filename = subobject.properties.at(0).toByteArray();
|
||||
filename = filename.mid(qMax(filename.lastIndexOf('\\'), filename.lastIndexOf('/')) + 1);
|
||||
|
||||
} else if (subobject.name == "Content" && !subobject.properties.isEmpty()) {
|
||||
content = subobject.properties.at(0).toByteArray();
|
||||
}
|
||||
}
|
||||
if (!content.isEmpty()) {
|
||||
textureContent.insert(filename, content);
|
||||
}
|
||||
} else if (object.name == "Material") {
|
||||
Material material = { glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3(1.0f, 1.0f, 1.0f), 96.0f };
|
||||
foreach (const FBXNode& subobject, object.children) {
|
||||
|
@ -1263,7 +1285,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
if (type.contains("diffuse")) {
|
||||
diffuseTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
||||
|
||||
} else if (type.contains("bump")) {
|
||||
} else if (type.contains("bump") || type.contains("normal")) {
|
||||
bumpTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
||||
|
||||
} else if (type == "lcl rotation") {
|
||||
|
@ -1463,23 +1485,23 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
if (materials.contains(childID)) {
|
||||
Material material = materials.value(childID);
|
||||
|
||||
QByteArray diffuseFilename;
|
||||
FBXTexture diffuseTexture;
|
||||
QString diffuseTextureID = diffuseTextures.value(childID);
|
||||
if (!diffuseTextureID.isNull()) {
|
||||
diffuseFilename = textureFilenames.value(diffuseTextureID);
|
||||
|
||||
diffuseTexture = getTexture(diffuseTextureID, textureFilenames, textureContent);
|
||||
|
||||
// FBX files generated by 3DSMax have an intermediate texture parent, apparently
|
||||
foreach (const QString& childTextureID, childMap.values(diffuseTextureID)) {
|
||||
if (textureFilenames.contains(childTextureID)) {
|
||||
diffuseFilename = textureFilenames.value(childTextureID);
|
||||
diffuseTexture = getTexture(diffuseTextureID, textureFilenames, textureContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray normalFilename;
|
||||
FBXTexture normalTexture;
|
||||
QString bumpTextureID = bumpTextures.value(childID);
|
||||
if (!bumpTextureID.isNull()) {
|
||||
normalFilename = textureFilenames.value(bumpTextureID);
|
||||
normalTexture = getTexture(bumpTextureID, textureFilenames, textureContent);
|
||||
generateTangents = true;
|
||||
}
|
||||
|
||||
|
@ -1489,21 +1511,21 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
part.diffuseColor = material.diffuse;
|
||||
part.specularColor = material.specular;
|
||||
part.shininess = material.shininess;
|
||||
if (!diffuseFilename.isNull()) {
|
||||
part.diffuseFilename = diffuseFilename;
|
||||
if (!diffuseTexture.filename.isNull()) {
|
||||
part.diffuseTexture = diffuseTexture;
|
||||
}
|
||||
if (!normalFilename.isNull()) {
|
||||
part.normalFilename = normalFilename;
|
||||
if (!normalTexture.filename.isNull()) {
|
||||
part.normalTexture = normalTexture;
|
||||
}
|
||||
}
|
||||
}
|
||||
materialIndex++;
|
||||
|
||||
} else if (textureFilenames.contains(childID)) {
|
||||
QByteArray filename = textureFilenames.value(childID);
|
||||
FBXTexture texture = getTexture(childID, textureFilenames, textureContent);
|
||||
for (int j = 0; j < extracted.partMaterialTextures.size(); j++) {
|
||||
if (extracted.partMaterialTextures.at(j).second == textureIndex) {
|
||||
extracted.mesh.parts[j].diffuseFilename = filename;
|
||||
extracted.mesh.parts[j].diffuseTexture = texture;
|
||||
}
|
||||
}
|
||||
textureIndex++;
|
||||
|
|
|
@ -103,6 +103,14 @@ public:
|
|||
glm::mat4 inverseBindMatrix;
|
||||
};
|
||||
|
||||
/// A texture map in an FBX document.
|
||||
class FBXTexture {
|
||||
public:
|
||||
|
||||
QByteArray filename;
|
||||
QByteArray content;
|
||||
};
|
||||
|
||||
/// A single part of a mesh (with the same material).
|
||||
class FBXMeshPart {
|
||||
public:
|
||||
|
@ -114,8 +122,8 @@ public:
|
|||
glm::vec3 specularColor;
|
||||
float shininess;
|
||||
|
||||
QByteArray diffuseFilename;
|
||||
QByteArray normalFilename;
|
||||
FBXTexture diffuseTexture;
|
||||
FBXTexture normalTexture;
|
||||
};
|
||||
|
||||
/// A single mesh (with optional blendshapes) extracted from an FBX document.
|
||||
|
|
Loading…
Reference in a new issue