Assimp: Extending the Library

assimp - Open Asset Import Library

Assimp  v3.1.1 (June 2014)
Extending the Library

Overview

Or - how to write your own loaders. It's easy. You just need to implement the Assimp::BaseImporter class, which defines a few abstract methods, register your loader, test it carefully and provide test models for it.

OK, that sounds too easy :-). The whole procedure for a new loader merely looks like this:

  • Create a header (FormatNameImporter.h) and a unit (FormatNameImporter.cpp) in the <root>/code/ directory
  • Add them to the following workspaces: vc8 and vc9 (the files are in the workspaces directory), CMAKE (code/CMakeLists.txt, create a new source group for your importer and put them also to ADD_LIBRARY( assimp SHARED))
  • Include AssimpPCH.h - this is the PCH file, and it includes already most Assimp-internal stuff.
  • Open Importer.cpp and include your header just below the (include_new_importers_here) line, guarded by a #define

    #if (!defined assimp_BUILD_NO_FormatName_IMPORTER)
    ...
    #endif

    Wrap the same guard around your .cpp!

  • Now advance to the (register_new_importers_here) line in the Importer.cpp and register your importer there - just like all the others do.
  • Setup a suitable test environment (i.e. use AssimpView or your own application), make sure to enable the aiProcess_ValidateDataStructure flag and enable verbose logging. That is, simply call before you import anything:
    DefaultLogger::create("AssimpLog.txt",Logger::VERBOSE)
  • Implement the Assimp::BaseImporter::CanRead(), Assimp::BaseImporter::InternReadFile() and Assimp::BaseImporter::GetExtensionList(). Just copy'n'paste the template from Appendix A and adapt it for your needs.
  • For error handling, throw a dynamic allocated ImportErrorException (see Appendix A) for critical errors, and log errors, warnings, infos and debuginfos with DefaultLogger::get()->[error, warn, debug, info].
  • Make sure that your loader compiles against all build configurations on all supported platforms. This includes -noboost! To avoid problems, see the boost section on this page for a list of all 'allowed' boost classes (again, this grew historically when we had to accept that boost is not THAT widely spread that one could rely on it being available everywhere).
  • Provide some free test models in <root>/test/models/<FormatName>/ and credit their authors. Test files for a file format shouldn't be too large (~500 KiB in total), and not too repetive. Try to cover all format features with test data.
  • Done! Please, share your loader that everyone can profit from it!

Properties

You can use properties to chance the behavior of you importer. In order to do so, you have to overide BaseImporter::SetupProperties, and specify you custom properties in config.h. Just have a look to the other AI_CONFIG_IMPORT_* defines and you will understand, how it works.

The properties can be set with Importer::SetProperty***() and can be accessed in your SetupProperties function with Importer::GetProperty***(). You can store the properties as a member variable of your importer, they are thread safe.

Notes for text importers

  • Try to make your parser as flexible as possible. Don't rely on particular layout, whitespace/tab style, except if the file format has a strict definition, in which case you should always warn about spec violations. But the general rule of thumb is be strict in what you write and tolerant in what you accept.
  • Call Assimp::BaseImporter::ConvertToUTF8() before you parse anything to convert foreign encodings to UTF-8. That's not necessary for XML importers, which must use the provided IrrXML for reading.

Notes for binary importers

  • Take care of endianess issues! Assimp importers mostly support big-endian platforms, which define the AI_BUILD_BIG_ENDIAN constant. See the next section for a list of utilities to simplify this task.
  • Don't trust the input data! Check all offsets!

Utilities

Mixed stuff for internal use by loaders, mostly documented (most of them are already included by AssimpPCH.h):

  • ByteSwapper (ByteSwapper.h) - manual byte swapping stuff for binary loaders.
  • StreamReader (StreamReader.h) - safe, endianess-correct, binary reading.
  • IrrXML (irrXMLWrapper.h) - for XML-parsing (SAX.
  • CommentRemover (RemoveComments.h) - remove single-line and multi-line comments from a text file.
  • fast_atof, strtoul10, strtoul16, SkipSpaceAndLineEnd, SkipToNextToken .. large family of low-level parsing functions, mostly declared in fast_atof.h, StringComparison.h and ParsingUtils.h (a collection that grew historically, so don't expect perfect organization).
  • ComputeNormalsWithSmoothingsGroups() (SmoothingGroups.h) - Computes normal vectors from plain old smoothing groups.
  • SkeletonMeshBuilder (SkeletonMeshBuilder.h) - generate a dummy mesh from a given (animation) skeleton.
  • StandardShapes (StandardShapes.h) - generate meshes for standard solids, such as platonic primitives, cylinders or spheres.
  • BatchLoader (BaseImporter.h) - manage imports from external files. Useful for file formats which spread their data across multiple files.
  • SceneCombiner (SceneCombiner.h) - exhaustive toolset to merge multiple scenes. Useful for file formats which spread their data across multiple files.

Filling materials

The required definitions zo set/remove/query keys in aiMaterial structures are declared in MaterialSystem.h, in a aiMaterial derivate called aiMaterial. The header is included by AssimpPCH.h, so you don't need to bother.

aiMaterial* mat = new aiMaterial();
const float spec = 16.f;
//set the name of the material:
NewMaterial->AddProperty(&aiString(MaterialName.c_str()), AI_MATKEY_NAME);//MaterialName is a std::string
//set the first diffuse texture
NewMaterial->AddProperty(&aiString(Texturename.c_str()), AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0));//again, Texturename is a std::string

Boost

The boost whitelist:

  • boost.scoped_ptr
  • boost.scoped_array
  • boost.format
  • boost.random
  • boost.common_factor
  • boost.foreach
  • boost.tuple

(if you happen to need something else, i.e. boost::thread, make this an optional feature. assimp_BUILD_BOOST_WORKAROUND is defined for -noboost builds)

Appendix A - Template for BaseImporter's abstract methods

// -------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool xxxxImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler,
bool checkSig) const
{
const std::string extension = GetExtension(pFile);
if(extension == "xxxx") {
return true;
}
if (!extension.length() || checkSig) {
// no extension given, or we're called a second time because no
// suitable loader was found yet. This means, we're trying to open
// the file and look for and hints to identify the file format.
// #Assimp::BaseImporter provides some utilities:
//
// #Assimp::BaseImporter::SearchFileHeaderForToken - for text files.
// It reads the first lines of the file and does a substring check
// against a given list of 'magic' strings.
//
// #Assimp::BaseImporter::CheckMagicToken - for binary files. It goes
// to a particular offset in the file and and compares the next words
// against a given list of 'magic' tokens.
// These checks MUST be done (even if !checkSig) if the file extension
// is not exclusive to your format. For example, .xml is very common
// and (co)used by many formats.
}
return false;
}
// -------------------------------------------------------------------------------
// Get list of file extensions handled by this loader
void xxxxImporter::GetExtensionList(std::set<std::string>& extensions)
{
extensions.insert("xxx");
}
// -------------------------------------------------------------------------------
void xxxxImporter::InternReadFile( const std::string& pFile,
aiScene* pScene, IOSystem* pIOHandler)
{
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
// Check whether we can read from the file
if( file.get() == NULL) {
throw DeadlyImportError( "Failed to open xxxx file " + pFile + ".");
}
// Your task: fill pScene
// Throw a ImportErrorException with a meaningful (!) error message if
// something goes wrong.
}
Generated on Sun Feb 21 2016 19:42:29 for Assimp by   doxygen 1.8.11