From 026cec7d7babc21422542dc31a2a8f7ca9671318 Mon Sep 17 00:00:00 2001 From: Alexander Gessler Date: Thu, 28 Jun 2012 01:49:52 +0200 Subject: [PATCH] - fbx: UVs, normals and materials arw now read properly. Fix bug related to reading vertices. --- code/FBXDocument.cpp | 60 ++++++++++++++++++++++++++++++++++++-------- code/FBXParser.cpp | 22 ++++++++-------- 2 files changed, 62 insertions(+), 20 deletions(-) diff --git a/code/FBXDocument.cpp b/code/FBXDocument.cpp index 05bb00858..15a9e4e45 100644 --- a/code/FBXDocument.cpp +++ b/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(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(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& uv_out = uvs[index]; - uv_out.resize(vertices.size()); std::vector 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 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(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 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(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(); } } diff --git a/code/FBXParser.cpp b/code/FBXParser.cpp index eca0f6609..39e7f7657 100644 --- a/code/FBXParser.cpp +++ b/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(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(t.end()-t.begin()); + std::copy(t.begin(),t.end(),temp); + temp[std::min(static_cast(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 ""; }