- fbx: read node hierarchy and assign model material indices. Cache already converted materials and meshes to make FBX instancing work as intended.
parent
17629f1ff1
commit
bf79c83bf2
|
@ -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;
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
Loading…
Reference in New Issue