From c7ee6fd70f1c2c67299137c62f293753009f7300 Mon Sep 17 00:00:00 2001
From: Alexander Gessler <alexander.gessler@gmx.net>
Date: Tue, 3 Jul 2012 20:41:21 +0200
Subject: [PATCH] - fbx: texture conversion & UV channel mapping.

---
 code/FBXConverter.cpp    | 110 +++++++++++++++++++++++++++++++++++++++
 code/FBXDocument.h       |   9 ++++
 code/FBXMeshGeometry.cpp |   6 +++
 3 files changed, 125 insertions(+)

diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp
index b07678bea..69083c4fe 100644
--- a/code/FBXConverter.cpp
+++ b/code/FBXConverter.cpp
@@ -50,6 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "FBXDocument.h"
 #include "FBXUtil.h"
 #include "FBXProperties.h"
+#include "FBXImporter.h"
 
 namespace Assimp {
 namespace FBX {
@@ -125,6 +126,8 @@ private:
 		aiMesh* out_mesh = new aiMesh();
 		meshes.push_back(out_mesh);
 
+		sourceMeshes.push_back(&mesh);
+
 		// copy vertices
 		out_mesh->mNumVertices = static_cast<size_t>(vertices.size());
 		out_mesh->mVertices = new aiVector3D[vertices.size()];
@@ -245,10 +248,115 @@ private:
 		str.Set(material.Name());
 		out_mat->AddProperty(&str,AI_MATKEY_NAME);
 
+		// shading stuff and colors
 		SetShadingPropertiesCommon(out_mat,props);
+	
+		// texture assignments
+		SetTextureProperties(out_mat,material.Textures());
 	}
 
 
+	// ------------------------------------------------------------------------------------------------
+	void TrySetTextureProperties(aiMaterial* out_mat, const TextureMap& textures, const std::string& propName, aiTextureType target)
+	{
+		TextureMap::const_iterator it = textures.find(propName);
+		if(it == textures.end()) {
+			return;
+		}
+
+		const Texture* const tex = (*it).second;
+		
+		aiString path;
+		path.Set(tex->RelativeFilename());
+
+		out_mat->AddProperty(&path,_AI_MATKEY_TEXTURE_BASE,target,0);
+
+		aiUVTransform uvTrafo;
+		// XXX handle all kinds of UV transformations
+		uvTrafo.mScaling = tex->UVScaling();
+		uvTrafo.mTranslation = tex->UVTranslation();
+		out_mat->AddProperty(&uvTrafo,1,_AI_MATKEY_UVTRANSFORM_BASE,target,0);
+
+		const PropertyTable& props = tex->Props();
+
+		int uvIndex = 0;
+
+		bool ok;
+		const std::string& uvSet = PropertyGet<std::string>(props,"UVSet",ok);
+		if(ok) {
+			// "default" is the name which usually appears in the FbxFileTexture template
+			if(uvSet != "default" && uvSet.length()) {
+				// this is a bit awkward - we need to find a mesh that uses this
+				// material and scan its UV channels for the given UV name because
+				// assimp references UV channels by index, not by name.
+
+				// XXX: the case that UV channels may appear in different orders
+				// in meshes is unhandled. A possible solution would be to sort
+				// the UV channels alphabetically, but this would have the side
+				// effect that the primary (first) UV channel would sometimes
+				// be moved, causing trouble when users read only the first
+				// UV channel and ignore UV channel assignments altogether.
+
+				const unsigned int matIndex = std::distance(materials.begin(), 
+					std::find(materials.begin(),materials.end(),out_mat)
+				);
+
+				uvIndex = -1;
+				BOOST_FOREACH(const MeshGeometry* mesh,sourceMeshes) {
+					ai_assert(mesh);
+
+					const std::vector<unsigned int>& mats = mesh->GetMaterialIndices();
+					if(std::find(mats.begin(),mats.end(),matIndex) == mats.end()) {
+						continue;
+					}
+
+					int index = -1;
+					for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+						if(mesh->GetTextureCoords(i).empty()) {
+							break;
+						}
+						const std::string& name = mesh->GetTextureCoordChannelName(i);
+						if(name == uvSet) {
+							index = static_cast<int>(i);
+							break;
+						}
+					}
+					if(index == -1) {
+						FBXImporter::LogWarn("did not found UV channel named " + uvSet + " in a mesh using this material");
+						continue;
+					}
+
+					if(uvIndex == -1) {
+						uvIndex = index;
+					}
+					else {
+						FBXImporter::LogWarn("the UV channel named " + uvSet + 
+							" appears at different positions in meshes, results will be wrong");
+					}
+				}
+			}
+		}
+
+		out_mat->AddProperty(&uvIndex,1,_AI_MATKEY_UVWSRC_BASE,target,0);
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	void SetTextureProperties(aiMaterial* out_mat, const TextureMap& textures)
+	{
+		TrySetTextureProperties(out_mat, textures, "DiffuseColor", aiTextureType_DIFFUSE);
+		TrySetTextureProperties(out_mat, textures, "AmbientColor", aiTextureType_AMBIENT);
+		TrySetTextureProperties(out_mat, textures, "EmissiveColor", aiTextureType_EMISSIVE);
+		TrySetTextureProperties(out_mat, textures, "SpecularColor", aiTextureType_SPECULAR);
+		TrySetTextureProperties(out_mat, textures, "TransparentColor", aiTextureType_OPACITY);
+		TrySetTextureProperties(out_mat, textures, "ReflectionColor", aiTextureType_REFLECTION);
+		TrySetTextureProperties(out_mat, textures, "DisplacementColor", aiTextureType_DISPLACEMENT);
+		TrySetTextureProperties(out_mat, textures, "NormalMap", aiTextureType_NORMALS);
+		TrySetTextureProperties(out_mat, textures, "Bump", aiTextureType_HEIGHT);
+	}
+
+
+
 	// ------------------------------------------------------------------------------------------------
 	aiColor3D GetColorPropertyFromMaterial(const PropertyTable& props,const std::string& baseName, bool& result)
 	{
@@ -355,6 +463,8 @@ private:
 	std::vector<aiMesh*> meshes;
 	std::vector<aiMaterial*> materials;
 
+	std::vector<const MeshGeometry*> sourceMeshes;
+
 	aiScene* const out;
 	const FBX::Document& doc;
 };
diff --git a/code/FBXDocument.h b/code/FBXDocument.h
index 2cd5cd16e..122ebe794 100644
--- a/code/FBXDocument.h
+++ b/code/FBXDocument.h
@@ -282,6 +282,13 @@ public:
 		return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? empty : uvs[index];
 	}
 
+
+	/** Get a UV coordinate slot, returns an empty array if
+	 *  the requested slot does not exist. */
+	std::string GetTextureCoordChannelName(unsigned int index) const {
+		return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? "" : uvNames[index];
+	}
+
 	/** Get a vertex color coordinate slot, returns an empty array if
 	 *  the requested slot does not exist. */
 	const std::vector<aiColor4D>& GetVertexColors(unsigned int index) const {
@@ -336,6 +343,8 @@ private:
 	std::vector<aiVector3D> tangents;
 	std::vector<aiVector3D> binormals;
 	std::vector<aiVector3D> normals;
+
+	std::string uvNames[AI_MAX_NUMBER_OF_TEXTURECOORDS];
 	std::vector<aiVector2D> uvs[AI_MAX_NUMBER_OF_TEXTURECOORDS];
 	std::vector<aiColor4D> colors[AI_MAX_NUMBER_OF_COLOR_SETS];
 
diff --git a/code/FBXMeshGeometry.cpp b/code/FBXMeshGeometry.cpp
index d066ed0e7..cbfe9442a 100644
--- a/code/FBXMeshGeometry.cpp
+++ b/code/FBXMeshGeometry.cpp
@@ -221,6 +221,12 @@ void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scop
 			return;
 		}
 
+		const Element* Name = source["Name"];
+		uvNames[index] = "";
+		if(Name) {
+			uvNames[index] = ParseTokenAsString(GetRequiredToken(*Name,0));
+		}
+
 		ReadVertexDataUV(uvs[index],source,
 			MappingInformationType,
 			ReferenceInformationType