diff --git a/code/AssetLib/Blender/BlenderLoader.cpp b/code/AssetLib/Blender/BlenderLoader.cpp index 4b094936c..04c911757 100644 --- a/code/AssetLib/Blender/BlenderLoader.cpp +++ b/code/AssetLib/Blender/BlenderLoader.cpp @@ -318,42 +318,81 @@ void BlenderImporter::ExtractScene(Scene &out, const FileDatabase &file) { #endif } +// ------------------------------------------------------------------------------------------------ +void BlenderImporter::ParseSubCollection(const Blender::Scene &in, aiNode *root, std::shared_ptr collection, ConversionData &conv_data) { + + std::deque root_objects; + // Count number of objects + for (std::shared_ptr cur = std::static_pointer_cast(collection->gobject.first); cur; cur = cur->next) { + if (cur->ob) { + root_objects.push_back(cur->ob); + } + } + std::deque root_children; + // Count number of child nodes + for (std::shared_ptr cur = std::static_pointer_cast(collection->children.first); cur; cur = cur->next) { + if (cur->collection) { + root_children.push_back(cur->collection.get()); + } + } + root->mNumChildren = static_cast(root_objects.size() + root_children.size()); + root->mChildren = new aiNode *[root->mNumChildren](); + + for (unsigned int i = 0; i < static_cast(root_objects.size()); ++i) { + root->mChildren[i] = ConvertNode(in, root_objects[i], conv_data, aiMatrix4x4()); + root->mChildren[i]->mParent = root; + } + + // For each subcollection create a new node to represent it + unsigned int iterator = static_cast(root_objects.size()); + for (std::shared_ptr cur = std::static_pointer_cast(collection->children.first); cur; cur = cur->next) { + if (cur->collection) { + root->mChildren[iterator] = new aiNode(cur->collection->id.name + 2); // skip over the name prefix 'OB' + root->mChildren[iterator]->mParent = root; + ParseSubCollection(in, root->mChildren[iterator], cur->collection, conv_data); + } + iterator += 1; + } +} + // ------------------------------------------------------------------------------------------------ void BlenderImporter::ConvertBlendFile(aiScene *out, const Scene &in, const FileDatabase &file) { ConversionData conv(file); - // FIXME it must be possible to take the hierarchy directly from - // the file. This is terrible. Here, we're first looking for - // all objects which don't have parent objects at all - - std::deque no_parents; - for (std::shared_ptr cur = std::static_pointer_cast(in.base.first); cur; cur = cur->next) { - if (cur->object) { - if (!cur->object->parent) { - no_parents.push_back(cur->object.get()); - } else { - conv.objects.insert(cur->object.get()); - } - } - } - for (std::shared_ptr cur = in.basact; cur; cur = cur->next) { - if (cur->object) { - if (cur->object->parent) { - conv.objects.insert(cur->object.get()); - } - } - } - - if (no_parents.empty()) { - ThrowException("Expected at least one object with no parent"); - } - aiNode *root = out->mRootNode = new aiNode(""); + // Iterate over all objects directly under master_collection, + // If in.master_collection == null, then we're parsing something older. + if (in.master_collection) { + ParseSubCollection(in, root, in.master_collection, conv); + } else { + std::deque no_parents; + for (std::shared_ptr cur = std::static_pointer_cast(in.base.first); cur; cur = cur->next) { + if (cur->object) { + if (!cur->object->parent) { + no_parents.push_back(cur->object.get()); + } else { + conv.objects.insert(cur->object.get()); + } + } + } + for (std::shared_ptr cur = in.basact; cur; cur = cur->next) { + if (cur->object) { + if (cur->object->parent) { + conv.objects.insert(cur->object.get()); + } + } + } - root->mNumChildren = static_cast(no_parents.size()); - root->mChildren = new aiNode *[root->mNumChildren](); - for (unsigned int i = 0; i < root->mNumChildren; ++i) { - root->mChildren[i] = ConvertNode(in, no_parents[i], conv, aiMatrix4x4()); - root->mChildren[i]->mParent = root; + if (no_parents.empty()) { + ThrowException("Expected at least one object with no parent"); + } + + root->mNumChildren = static_cast(no_parents.size()); + root->mChildren = new aiNode *[root->mNumChildren](); + for (unsigned int i = 0; i < root->mNumChildren; ++i) { + root->mChildren[i] = ConvertNode(in, no_parents[i], conv, aiMatrix4x4()); + root->mChildren[i]->mParent = root; + } } BuildMaterials(conv); diff --git a/code/AssetLib/Blender/BlenderLoader.h b/code/AssetLib/Blender/BlenderLoader.h index 1df0fcf90..94f034cd1 100644 --- a/code/AssetLib/Blender/BlenderLoader.h +++ b/code/AssetLib/Blender/BlenderLoader.h @@ -78,6 +78,7 @@ struct ElemBase; namespace Blender { struct Scene; struct Object; +struct Collection; struct Mesh; struct Camera; struct Lamp; @@ -116,6 +117,7 @@ protected: void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override; void ParseBlendFile(Blender::FileDatabase &out, std::shared_ptr stream); void ExtractScene(Blender::Scene &out, const Blender::FileDatabase &file); + void ParseSubCollection(const Blender::Scene &in, aiNode *root, std::shared_ptr collection, Blender::ConversionData &conv_data); void ConvertBlendFile(aiScene *out, const Blender::Scene &in, const Blender::FileDatabase &file); private: diff --git a/code/AssetLib/Blender/BlenderScene.cpp b/code/AssetLib/Blender/BlenderScene.cpp index c654b5b14..c93d913fc 100644 --- a/code/AssetLib/Blender/BlenderScene.cpp +++ b/code/AssetLib/Blender/BlenderScene.cpp @@ -94,6 +94,52 @@ void Structure ::Convert( db.reader->IncPtr(size); } +//-------------------------------------------------------------------------------- +template <> +void Structure::Convert( + CollectionObject &dest, + const FileDatabase &db) const { + + ReadFieldPtr(dest.next, "*next", db); + { + //std::shared_ptr prev; + //ReadFieldPtr(prev, "*prev", db); + //dest.prev = prev.get(); + + std::shared_ptr ob; + ReadFieldPtr(ob, "*ob", db); + dest.ob = ob.get(); + } + + db.reader->IncPtr(size); +} + +//-------------------------------------------------------------------------------- +template <> +void Structure::Convert( + CollectionChild &dest, + const FileDatabase &db) const { + + ReadFieldPtr(dest.prev, "*prev", db); + ReadFieldPtr(dest.next, "*next", db); + ReadFieldPtr(dest.collection, "*collection", db); + + db.reader->IncPtr(size); +} + +//-------------------------------------------------------------------------------- +template <> +void Structure::Convert( + Collection &dest, + const FileDatabase &db) const { + + ReadField(dest.id, "id", db); + ReadField(dest.gobject, "gobject", db); + ReadField(dest.children, "children", db); + + db.reader->IncPtr(size); +} + //-------------------------------------------------------------------------------- template <> void Structure ::Convert( @@ -660,6 +706,7 @@ void Structure ::Convert( ReadFieldPtr(dest.camera, "*camera", db); ReadFieldPtr(dest.world, "*world", db); ReadFieldPtr(dest.basact, "*basact", db); + ReadFieldPtr(dest.master_collection, "*master_collection", db); ReadField(dest.base, "base", db); db.reader->IncPtr(size); @@ -833,6 +880,9 @@ void DNA::RegisterConverters() { converters["Image"] = DNA::FactoryPair(&Structure::Allocate, &Structure::Convert); converters["CustomData"] = DNA::FactoryPair(&Structure::Allocate, &Structure::Convert); converters["CustomDataLayer"] = DNA::FactoryPair(&Structure::Allocate, &Structure::Convert); + converters["Collection"] = DNA::FactoryPair(&Structure::Allocate, &Structure::Convert); + converters["CollectionChild"] = DNA::FactoryPair(&Structure::Allocate, &Structure::Convert); + converters["CollectionObject"] = DNA::FactoryPair(&Structure::Allocate, &Structure::Convert); } #endif // ASSIMP_BUILD_NO_BLEND_IMPORTER diff --git a/code/AssetLib/Blender/BlenderScene.h b/code/AssetLib/Blender/BlenderScene.h index 50dda28c8..ebc6a27e6 100644 --- a/code/AssetLib/Blender/BlenderScene.h +++ b/code/AssetLib/Blender/BlenderScene.h @@ -107,6 +107,7 @@ namespace Blender { struct Object; struct MTex; struct Image; +struct Collection; #include @@ -147,6 +148,26 @@ struct Group : ElemBase { std::shared_ptr gobject; }; +// ------------------------------------------------------------------------------- +struct CollectionObject : ElemBase { + //CollectionObject* prev; + std::shared_ptr next; + Object *ob; +}; + +// ------------------------------------------------------------------------------- +struct CollectionChild : ElemBase { + std::shared_ptr next, prev; + std::shared_ptr collection; +}; + +// ------------------------------------------------------------------------------- +struct Collection : ElemBase { + ID id FAIL; + ListBase gobject; // CollectionObject + ListBase children; // CollectionChild +}; + // ------------------------------------------------------------------------------- struct World : ElemBase { ID id FAIL; @@ -729,11 +750,12 @@ struct Scene : ElemBase { std::shared_ptr camera WARN; std::shared_ptr world WARN; std::shared_ptr basact WARN; + std::shared_ptr master_collection WARN; ListBase base; Scene() : - ElemBase(), camera(), world(), basact() { + ElemBase(), camera(), world(), basact(), master_collection() { // empty } }; diff --git a/code/AssetLib/Blender/BlenderSceneGen.h b/code/AssetLib/Blender/BlenderSceneGen.h index 0c93d17a3..762fdd33b 100644 --- a/code/AssetLib/Blender/BlenderSceneGen.h +++ b/code/AssetLib/Blender/BlenderSceneGen.h @@ -62,6 +62,12 @@ template <> void Structure :: Convert ( ) const ; +template <> void Structure::Convert( + Collection& dest, + const FileDatabase& db + ) const +; + template <> void Structure :: Convert ( MTex& dest, const FileDatabase& db diff --git a/test/models/BLEND/BlenderDefault_276.blend b/test/models/BLEND/BlenderDefault_276.blend new file mode 100644 index 000000000..28ebbcc1f Binary files /dev/null and b/test/models/BLEND/BlenderDefault_276.blend differ diff --git a/test/models/BLEND/box.blend b/test/models/BLEND/box.blend index 28ebbcc1f..a41250fdc 100644 Binary files a/test/models/BLEND/box.blend and b/test/models/BLEND/box.blend differ diff --git a/test/unit/utBlenderImportExport.cpp b/test/unit/utBlenderImportExport.cpp index 8a585ed74..9ef1fc693 100644 --- a/test/unit/utBlenderImportExport.cpp +++ b/test/unit/utBlenderImportExport.cpp @@ -114,6 +114,12 @@ TEST(utBlenderImporter, importBlenderDefault271) { ASSERT_NE(nullptr, scene); } +TEST(utBlenderImporter, importBlenderDefault293) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/BLEND/BlenderDefault_276.blend", aiProcess_ValidateDataStructure); + ASSERT_NE(nullptr, scene); +} + TEST(utBlenderImporter, importCubeHierarchy_248) { Assimp::Importer importer; const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/BLEND/CubeHierarchy_248.blend", aiProcess_ValidateDataStructure);