Browse Source

- fbx: UVs, normals and materials arw now read properly. Fix bug related to reading vertices.

Alexander Gessler 13 years ago
parent
commit
026cec7d7b
2 changed files with 62 additions and 20 deletions
  1. 50 10
      code/FBXDocument.cpp
  2. 12 10
      code/FBXParser.cpp

+ 50 - 10
code/FBXDocument.cpp

@@ -90,7 +90,7 @@ const Scope& GetRequiredScope(const Element& el)
 const Token& GetRequiredToken(const Element& el, unsigned int index)
 {
 	const TokenList& t = el.Tokens();
-	if(t.size() > index) {
+	if(index >= t.size()) {
 		DOMError(Formatter::format( "missing token at index " ) << index,&el);
 	}
 
@@ -292,12 +292,9 @@ const Object* LazyObject::Get()
 	} 
 
 	// this needs to be relatively fast since we do it a lot,
-	// so avoid constructing strings all the time. strcmp()
-	// may scan beyond the bounds of the token, but the 
-	// next character is always a colon so false positives
-	// are not possible.
+	// so avoid constructing strings all the time.
 	const char* obtype = key.begin();
-	if (!strcmp(obtype,"Geometry")) {
+	if (!strncmp(obtype,"Geometry",static_cast<size_t>(key.end()-key.begin()))) {
 
 		if (!strcmp(classtag.c_str(),"Mesh")) {
 			object = new MeshGeometry(element,name);
@@ -305,7 +302,7 @@ const Object* LazyObject::Get()
 	}
 
 	if (!object.get()) {
-		DOMError("failed to convert element to DOM object, class: " + classtag + ", name: " + name,&element);
+		//DOMError("failed to convert element to DOM object, class: " + classtag + ", name: " + name,&element);
 	}
 
 	return object.get();
@@ -370,11 +367,17 @@ MeshGeometry::MeshGeometry(const Element& element, const std::string& name)
 	mapping_counts.resize(tempVerts.size(),0);
 	mappings.resize(tempFaces.size());
 
+	const size_t vertex_count = tempVerts.size();
+
 	// generate output vertices, computing an adjacency table to
 	// preserve the mapping from fbx indices to *this* indexing.
 	unsigned int count = 0;
 	BOOST_FOREACH(int index, tempFaces) {
-		const int absi = index < 0 ? -index : index;
+		const int absi = index < 0 ? (-index - 1) : index;
+		if(static_cast<size_t>(absi) >= vertex_count) {
+			DOMError("polygon vertex index out of range",&PolygonVertexIndex);
+		}
+
 		vertices.push_back(tempVerts[absi]);
 		++count;
 
@@ -396,7 +399,7 @@ MeshGeometry::MeshGeometry(const Element& element, const std::string& name)
 
 	cursor = 0;
 	BOOST_FOREACH(int index, tempFaces) {
-		const int absi = index < 0 ? -index : index;
+		const int absi = index < 0 ? (-index - 1) : index;
 		mappings[mapping_offsets[absi] + mapping_counts[absi]++] = cursor;
 	}
 
@@ -475,12 +478,15 @@ void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scop
 		}
 
 		std::vector<aiVector2D>& uv_out = uvs[index];
-		uv_out.resize(vertices.size());
 
 		std::vector<aiVector2D> tempUV;
 		ReadVectorDataArray(tempUV,GetRequiredElement(source,"UV"));
 
+		// handle permutations of Mapping and Reference type - it would be nice to
+		// deal with this more elegantly and with less redundancy, but right
+		// now it seems unavoidable.
 		if (MappingInformationType == "ByVertice" && ReferenceInformationType == "Direct") {	
+			uv_out.resize(vertices.size());
 			for (size_t i = 0, e = tempUV.size(); i < e; ++i) {
 
 				const unsigned int istart = mapping_offsets[i], iend = istart + mapping_counts[i];
@@ -490,6 +496,7 @@ void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scop
 			}
 		}
 		else if (MappingInformationType == "ByVertice" && ReferenceInformationType == "IndexToDirect") {	
+			uv_out.resize(vertices.size());
 
 			std::vector<int> uvIndices;
 			ReadIntDataArray(uvIndices,GetRequiredElement(source,"UVIndex"));
@@ -498,10 +505,41 @@ void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scop
 
 				const unsigned int istart = mapping_offsets[i], iend = istart + mapping_counts[i];
 				for (unsigned int j = istart; j < iend; ++j) {
+					if(static_cast<size_t>(uvIndices[i]) >= tempUV.size()) {
+						DOMError("UV index out of range",&GetRequiredElement(source,"UVIndex"));
+					}
 					uv_out[mappings[j]] = tempUV[uvIndices[i]];
 				}
 			}
 		}
+		else if (MappingInformationType == "ByPolygonVertex" && ReferenceInformationType == "Direct") {	
+			if (tempUV.size() != vertices.size()) {
+				FBXImporter::LogError("size of input UV array unexpected for ByPolygonVertex mapping");
+				return;
+			}
+
+			uv_out.swap(tempUV);
+		}
+		else if (MappingInformationType == "ByPolygonVertex" && ReferenceInformationType == "IndexToDirect") {	
+			uv_out.resize(vertices.size());
+
+			std::vector<int> uvIndices;
+			ReadIntDataArray(uvIndices,GetRequiredElement(source,"UVIndex"));
+			
+			if (uvIndices.size() != vertices.size()) {
+				FBXImporter::LogError("size of input UV array unexpected for ByPolygonVertex mapping");
+				return;
+			}
+
+			unsigned int next = 0;
+			BOOST_FOREACH(int i, uvIndices) {
+				if(static_cast<size_t>(i) >= tempUV.size()) {
+					DOMError("UV index out of range",&GetRequiredElement(source,"UVIndex"));
+				}
+
+				uv_out[next++] = tempUV[i];
+			}
+		}
 		else {
 			FBXImporter::LogError(Formatter::format("ignoring normals, unrecognized access type: ") 
 				<< MappingInformationType << "," << ReferenceInformationType);
@@ -594,6 +632,8 @@ Document::Document(const Parser& parser)
 		}
 
 		objects[id] = new LazyObject(*el.second);
+		// DEBUG - evaluate all objects
+		const Object* o = objects[id]->Get();
 	}
 }
 

+ 12 - 10
code/FBXParser.cpp

@@ -223,7 +223,7 @@ uint64_t ParseTokenAsID(const Token& t, const char*& err_out)
 
 	const char* out;
 	const uint64_t id = strtoul10_64(t.begin(),&out,&length);
-	if (out != t.end()) {
+	if (out > t.end()) {
 		err_out = "failed to parse ID";
 		return 0L;
 	}
@@ -257,7 +257,7 @@ size_t ParseTokenAsDim(const Token& t, const char*& err_out)
 
 	const char* out;
 	const size_t id = static_cast<size_t>(strtoul10_64(t.begin() + 1,&out,&length));
-	if (out != t.end()) {
+	if (out > t.end()) {
 		err_out = "failed to parse ID";
 		return 0;
 	}
@@ -278,14 +278,16 @@ float ParseTokenAsFloat(const Token& t, const char*& err_out)
 
 	const char* inout = t.begin();
 
-	float f;
-	fast_atof(&inout);
-	if (inout != t.end()) {
-		err_out = "failed to parse floating point number";
-		return 0.0f;
-	}
+	// need to copy the input string to a temporary buffer
+	// first - next in the fbx token stream comes ',', 
+	// which fast_atof could interpret as decimal point.
+#define MAX_FLOAT_LENGTH 31
+	char temp[MAX_FLOAT_LENGTH + 1];
+	const size_t length = static_cast<size_t>(t.end()-t.begin());
+	std::copy(t.begin(),t.end(),temp);
+	temp[std::min(static_cast<size_t>(MAX_FLOAT_LENGTH),length)] = '\0';
 
-	return f;
+	return fast_atof(temp);
 }
 
 
@@ -329,7 +331,7 @@ std::string ParseTokenAsString(const Token& t, const char*& err_out)
 	}
 
 	const char* s = t.begin(), *e = t.end() - 1;
-	if (*s != '\"' || *e != '\*') {
+	if (*s != '\"' || *e != '\"') {
 		err_out = "expected double quoted string";
 		return "";
 	}