- fbx: read node hierarchy and assign model material indices. Cache already converted materials and meshes to make FBX instancing work as intended.

pull/14/head
Alexander Gessler 2012-07-04 16:33:21 +02:00
parent 17629f1ff1
commit bf79c83bf2
5 changed files with 1579 additions and 1464 deletions

View File

@ -70,20 +70,7 @@ public:
: out(out) : out(out)
, doc(doc) , doc(doc)
{ {
//ConvertRootNode(); ConvertRootNode();
// hack to process all meshes
BOOST_FOREACH(const ObjectMap::value_type& v,doc.Objects()) {
const Object* ob = v.second->Get();
if(!ob) {
continue;
}
const MeshGeometry* geo = dynamic_cast<const MeshGeometry*>(ob);
if(geo) {
ConvertMesh(*geo);
}
}
if(doc.Settings().readAllMaterials) { if(doc.Settings().readAllMaterials) {
// unfortunately this means we have to evaluate all objects // unfortunately this means we have to evaluate all objects
@ -129,24 +116,112 @@ private:
// find scene root and trigger recursive scene conversion // find scene root and trigger recursive scene conversion
void ConvertRootNode() void ConvertRootNode()
{ {
out->mRootNode = new aiNode();
out->mRootNode->mName.Set("Model::RootNode");
// root has ID 0
ConvertNodes(0L, *out->mRootNode);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// MeshGeometry -> aiMesh // collect and assign child nodes
void ConvertMesh(const MeshGeometry& mesh) void ConvertNodes(uint64_t id, aiNode& parent)
{ {
const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(id);
std::vector<aiNode*> nodes;
nodes.reserve(conns.size());
BOOST_FOREACH(const Connection* con, conns) {
// ignore object-property links
if(con->PropertyName().length()) {
continue;
}
const Object* const object = con->SourceObject();
if(!object) {
FBXImporter::LogWarn("failed to convert source object for node link");
continue;
}
const Model* const model = dynamic_cast<const Model*>(object);
if(model) {
aiNode* nd = new aiNode();
nd->mName.Set(model->Name());
nd->mParent = &parent;
// XXX handle transformation
ConvertModel(*model, *nd);
}
}
if(nodes.size()) {
parent.mChildren = new aiNode*[nodes.size()]();
parent.mNumChildren = static_cast<unsigned int>(nodes.size());
std::swap_ranges(nodes.begin(),nodes.end(),parent.mChildren);
}
}
// ------------------------------------------------------------------------------------------------
void ConvertModel(const Model& model, aiNode& nd)
{
const std::vector<const Geometry*>& geos = model.GetGeometry();
std::vector<unsigned int> meshes;
meshes.reserve(geos.size());
BOOST_FOREACH(const Geometry* geo, geos) {
const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*>(geo);
if(mesh) {
const unsigned int index = ConvertMesh(*mesh, model);
if(index == 0) {
continue;
}
meshes.push_back(index-1);
}
else {
FBXImporter::LogWarn("ignoring unrecognized geometry: " + geo->Name());
}
}
if(meshes.size()) {
nd.mMeshes = new unsigned int[meshes.size()]();
nd.mNumMeshes = static_cast<unsigned int>(meshes.size());
std::swap_ranges(meshes.begin(),meshes.end(),nd.mMeshes);
}
}
// ------------------------------------------------------------------------------------------------
// MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed
unsigned int ConvertMesh(const MeshGeometry& mesh, const Model& model)
{
MeshMap::const_iterator it = meshes_converted.find(&mesh);
if (it != meshes_converted.end()) {
return (*it).second + 1;
}
const std::vector<aiVector3D>& vertices = mesh.GetVertices(); const std::vector<aiVector3D>& vertices = mesh.GetVertices();
const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts(); const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
if(vertices.empty() || faces.empty()) { if(vertices.empty() || faces.empty()) {
return; FBXImporter::LogWarn("ignoring empty geometry: " + mesh.Name());
return 0;
} }
aiMesh* out_mesh = new aiMesh(); aiMesh* out_mesh = new aiMesh();
meshes.push_back(out_mesh); meshes.push_back(out_mesh);
sourceMeshes.push_back(&mesh); meshes_converted[&mesh] = static_cast<unsigned int>(meshes.size()-1);
// copy vertices // copy vertices
out_mesh->mNumVertices = static_cast<size_t>(vertices.size()); out_mesh->mNumVertices = static_cast<size_t>(vertices.size());
@ -251,22 +326,32 @@ private:
} }
const std::vector<unsigned int>& mindices = mesh.GetMaterialIndices(); const std::vector<unsigned int>& mindices = mesh.GetMaterialIndices();
ConvertMaterialForMesh(out_mesh,mesh,mindices.size() ? mindices[0] : 9); ConvertMaterialForMesh(out_mesh,model,mesh,mindices.size() ? mindices[0] : 0);
return static_cast<unsigned int>(meshes.size());
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ConvertMaterialForMesh(aiMesh* out, const MeshGeometry& geo, unsigned int materialIndex) void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo, unsigned int materialIndex)
{ {
// locate source materials for this mesh // locate source materials for this mesh
const std::vector<const Material*>& mats = geo.GetMaterials(); const std::vector<const Material*>& mats = model.GetMaterials();
if (materialIndex >= mats.size()) { if (materialIndex >= mats.size()) {
FBXImporter::LogError("material index out of bounds, ignoring"); FBXImporter::LogError("material index out of bounds, ignoring");
out->mMaterialIndex = GetDefaultMaterial(); out->mMaterialIndex = GetDefaultMaterial();
return; return;
} }
out->mMaterialIndex = ConvertMaterial(*mats[materialIndex]); const Material* const mat = mats[materialIndex];
MaterialMap::const_iterator it = materials_converted.find(mat);
if (it != materials_converted.end()) {
out->mMaterialIndex = (*it).second;
return;
}
out->mMaterialIndex = ConvertMaterial(*mat);
materials_converted[mat] = out->mMaterialIndex;
} }
@ -301,8 +386,9 @@ private:
// generate empty output material // generate empty output material
aiMaterial* out_mat = new aiMaterial(); aiMaterial* out_mat = new aiMaterial();
materials_converted[&material] = static_cast<unsigned int>(materials.size());
materials.push_back(out_mat); materials.push_back(out_mat);
materials_converted.insert(&material);
aiString str; aiString str;
@ -366,8 +452,11 @@ private:
); );
uvIndex = -1; uvIndex = -1;
BOOST_FOREACH(const MeshGeometry* mesh,sourceMeshes) { BOOST_FOREACH(const MeshMap::value_type& v,meshes_converted) {
ai_assert(mesh); const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*> (v.first);
if(!mesh) {
continue;
}
const std::vector<unsigned int>& mats = mesh->GetMaterialIndices(); const std::vector<unsigned int>& mats = mesh->GetMaterialIndices();
if(std::find(mats.begin(),mats.end(),matIndex) == mats.end()) { if(std::find(mats.begin(),mats.end(),matIndex) == mats.end()) {
@ -386,7 +475,7 @@ private:
} }
} }
if(index == -1) { if(index == -1) {
FBXImporter::LogWarn("did not found UV channel named " + uvSet + " in a mesh using this material"); FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material");
continue; continue;
} }
@ -398,6 +487,11 @@ private:
" appears at different positions in meshes, results will be wrong"); " appears at different positions in meshes, results will be wrong");
} }
} }
if(uvIndex == -1) {
FBXImporter::LogWarn("failed to resolve UV channel " + uvSet + ", using first UV channel");
uvIndex = 0;
}
} }
} }
@ -530,9 +624,11 @@ private:
std::vector<aiMesh*> meshes; std::vector<aiMesh*> meshes;
std::vector<aiMaterial*> materials; std::vector<aiMaterial*> materials;
std::set<const Material*> materials_converted; typedef std::map<const Material*, unsigned int> MaterialMap;
MaterialMap materials_converted;
std::vector<const MeshGeometry*> sourceMeshes; typedef std::map<const Geometry*, unsigned int> MeshMap;
MeshMap meshes_converted;
aiScene* const out; aiScene* const out;
const FBX::Document& doc; const FBX::Document& doc;

View File

@ -413,6 +413,9 @@ const Object* LazyObject::Get()
object.reset(new MeshGeometry(id,element,name,doc)); object.reset(new MeshGeometry(id,element,name,doc));
} }
} }
else if (!strncmp(obtype,"Model",length)) {
object.reset(new Model(id,element,doc,name));
}
else if (!strncmp(obtype,"Material",length)) { else if (!strncmp(obtype,"Material",length)) {
object.reset(new Material(id,element,doc,name)); object.reset(new Material(id,element,doc,name));
} }
@ -603,7 +606,8 @@ void Document::ReadConnections()
continue; continue;
} }
if(objects.find(dest) == objects.end()) { // dest may be 0 (root node)
if(dest && objects.find(dest) == objects.end()) {
DOMWarning("destination object for connection does not exist",&el); DOMWarning("destination object for connection does not exist",&el);
continue; continue;
} }
@ -669,7 +673,8 @@ Connection::Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, co
, doc(doc) , doc(doc)
{ {
ai_assert(doc.Objects().find(src) != doc.Objects().end()); ai_assert(doc.Objects().find(src) != doc.Objects().end());
ai_assert(doc.Objects().find(dest) != doc.Objects().end()); // dest may be 0 (root node)
ai_assert(!dest || doc.Objects().find(dest) != doc.Objects().end());
} }

View File

@ -57,6 +57,8 @@ namespace FBX {
class PropertyTable; class PropertyTable;
class Document; class Document;
class Material;
class Geometry;
/** Represents a delay-parsed FBX objects. Many objects in the scene /** Represents a delay-parsed FBX objects. Many objects in the scene
@ -123,6 +125,54 @@ protected:
}; };
/** DOM base class for FBX models */
class Model : public Object
{
public:
Model(uint64_t id, const Element& element, const Document& doc, const std::string& name);
~Model();
public:
const std::string& Shading() const {
return shading;
}
const std::string& Culling() const {
return culling;
}
const PropertyTable& Props() const {
ai_assert(props.get());
return *props.get();
}
/** Get material links */
const std::vector<const Material*>& GetMaterials() const {
return materials;
}
/** Get geometry links */
const std::vector<const Geometry*>& GetGeometry() const {
return geometry;
}
private:
void ResolveLinks(const Element& element, const Document& doc);
private:
std::vector<const Material*> materials;
std::vector<const Geometry*> geometry;
std::string shading;
std::string culling;
boost::shared_ptr<const PropertyTable> props;
};
/** DOM class for generic FBX textures */ /** DOM class for generic FBX textures */
@ -302,16 +352,10 @@ public:
return materials; return materials;
} }
/** Get per-face-vertex material assignments */
const std::vector<const Material*>& GetMaterials() const {
return materials_resolved;
}
public: public:
private: private:
void ResolveMaterialLinks(const Element& element, const Document& doc);
void ReadLayer(const Scope& layer); void ReadLayer(const Scope& layer);
void ReadLayerElement(const Scope& layerElement); void ReadLayerElement(const Scope& layerElement);
void ReadVertexData(const std::string& type, int index, const Scope& source); void ReadVertexData(const std::string& type, int index, const Scope& source);
@ -342,8 +386,6 @@ private:
private: private:
std::vector<const Material*> materials_resolved;
// cached data arrays // cached data arrays
std::vector<unsigned int> materials; std::vector<unsigned int> materials;
std::vector<aiVector3D> vertices; std::vector<aiVector3D> vertices;

View File

@ -156,8 +156,6 @@ MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::strin
FBXImporter::LogWarn("ignoring additional geometry layers"); FBXImporter::LogWarn("ignoring additional geometry layers");
} }
} }
ResolveMaterialLinks(element,doc);
} }
@ -168,36 +166,6 @@ MeshGeometry::~MeshGeometry()
} }
// ------------------------------------------------------------------------------------------------
void MeshGeometry::ResolveMaterialLinks(const Element& element, const Document& doc)
{
// resolve material
const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID());
materials_resolved.reserve(conns.size());
BOOST_FOREACH(const Connection* con, conns) {
// material links should be Object-Object connections
if (con->PropertyName().length()) {
continue;
}
const Object* const ob = con->SourceObject();
if(!ob) {
DOMWarning("failed to read source object for material link, ignoring",&element);
continue;
}
const Material* const mat = dynamic_cast<const Material*>(ob);
if(!mat) {
DOMWarning("source object for material link is not a material, ignoring",&element);
continue;
}
materials_resolved.push_back(mat);
}
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void MeshGeometry::ReadLayer(const Scope& layer) void MeshGeometry::ReadLayer(const Scope& layer)

File diff suppressed because it is too large Load Diff