diff --git a/.gitignore b/.gitignore index 7fe107086..4ddf06e63 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,34 @@ build .project *.kdev4* + +# Visual Studio +*.sln +*.ncb +*.vcproj + +# Output +bin/ +lib/ +contrib/ + +# Generated +assimp.pc +revision.h +contrib/zlib/zconf.h +contrib/zlib/zlib.pc + +# CMake +CMakeCache.txt +CMakeFiles +cmake_install.cmake +cmake_uninstall.cmake +*.dir/ +assimp-config.cmake +assimp-config-version.cmake + +# Tests +test/results + +# Python +__pycache__ diff --git a/CHANGES b/CHANGES index da5f36e37..b26103bf2 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,29 @@ CHANGELOG ---------------------------------------------------------------------- +3.1.1 (2014-06-15) + +FEATURES: + - Support for FBX 2013 and newer, binary and ASCII (this is partly + work from Google Summer of Code 2012) + - Support for OGRE binary mesh and skeleton format + - Updated BLEND support for newer Blender versions + - Support for arbitrary meta data, used to hold FBX and DAE metadata + - OBJ Export now produces smaller files + - Meshes can now have names, this is supported by the major importers + - Improved IFC geometry generation + - M3 support has been removed + +FIXES/HOUSEKEEPING: + - Hundreds of bugfixes in all parts of the library + - CMake is now the primary build system + +API COMPATIBILITY: + - 3.1.1 is not binary compatible to 3.0 due to aiNode::mMetaData + and aiMesh::mName + - Export interface has been cleaned up and unified + - Other than that no relevant changes + 3.0 (2012-07-07) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6fef21202..31561864f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ PROJECT( Assimp ) # Define here the needed parameters set (ASSIMP_VERSION_MAJOR 3) -set (ASSIMP_VERSION_MINOR 0) +set (ASSIMP_VERSION_MINOR 1) set (ASSIMP_VERSION_PATCH 1) # subversion revision? set (ASSIMP_VERSION ${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}.${ASSIMP_VERSION_PATCH}) set (ASSIMP_SOVERSION 3) @@ -32,11 +32,11 @@ if(NOT GIT_COMMIT_HASH) endif(NOT GIT_COMMIT_HASH) configure_file( - ${CMAKE_SOURCE_DIR}/revision.h.in - ${CMAKE_BINARY_DIR}/revision.h + ${CMAKE_CURRENT_SOURCE_DIR}/revision.h.in + ${CMAKE_CURRENT_BINARY_DIR}/revision.h ) -include_directories(${CMAKE_BINARY_DIR}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) option(ASSIMP_OPT_BUILD_PACKAGES "Set to ON to generate CPack configuration files and packaging targets" OFF) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules" ) @@ -76,11 +76,18 @@ SET( ASSIMP_INCLUDE_INSTALL_DIR "include" CACHE PATH "Path the header files are installed to." ) SET( ASSIMP_BIN_INSTALL_DIR "bin" CACHE PATH "Path the tool executables are installed to." ) +SET ( ASSIMP_BUILD_STATIC_LIB OFF CACHE BOOL + "Build a static (.a) version of the library" ) SET(ASSIMP_DEBUG_POSTFIX "d" CACHE STRING "Debug Postfitx for lib, samples and tools") # Allow the user to build a static library option ( BUILD_SHARED_LIBS "Build a shared version of the library" ON ) +IF ( ASSIMP_BUILD_STATIC_LIB ) + option ( BUILD_SHARED_LIBS "Build a shared version of the library" OFF ) +ELSE ( ASSIMP_BUILD_STATIC_LIB ) + option ( BUILD_SHARED_LIBS "Build a shared version of the library" ON ) +ENDIF ( ASSIMP_BUILD_STATIC_LIB ) # Generate a pkg-config .pc for the Assimp library. CONFIGURE_FILE( "${PROJECT_SOURCE_DIR}/assimp.pc.in" "${PROJECT_BINARY_DIR}/assimp.pc" @ONLY ) @@ -104,7 +111,7 @@ IF ( ASSIMP_ENABLE_BOOST_WORKAROUND ) MESSAGE( STATUS "Building a non-boost version of Assimp." ) ELSE ( ASSIMP_ENABLE_BOOST_WORKAROUND ) SET( Boost_DETAILED_FAILURE_MSG ON ) - SET( Boost_ADDITIONAL_VERSIONS "1.47" "1.47.0" "1.48.0" "1.48" "1.49" "1.49.0" "1.50" "1.50.0" "1.51" "1.51.0" "1.52.0" "1.53.0" "1.54.0") + SET( Boost_ADDITIONAL_VERSIONS "1.47" "1.47.0" "1.48.0" "1.48" "1.49" "1.49.0" "1.50" "1.50.0" "1.51" "1.51.0" "1.52.0" "1.53.0" "1.54.0" "1.55" ) FIND_PACKAGE( Boost ) IF ( NOT Boost_FOUND ) MESSAGE( FATAL_ERROR diff --git a/CREDITS b/CREDITS index f75f0ff29..5d45a9e92 100644 --- a/CREDITS +++ b/CREDITS @@ -3,8 +3,8 @@ Open Asset Import Library (Assimp) Developers and Contributors =============================================================== -The following is the list of all constributors. -Thanks for your help! +The following is a non-exhaustive list of all constributors over the years. +If you think your name should be listed here, drop us a line and we'll add you. - Alexander Gessler, 3DS-, BLEND-, ASE-, DXF-, HMP-, MDL-, MD2-, MD3-, MD5-, MDC-, NFF-, PLY-, STL-, RAW-, OFF-, MS3D-, Q3D- and LWO-Loader, Assimp-Viewer, assimp-cmd, -noboost, Website (Admin and Design). @@ -41,7 +41,7 @@ Submitted patches to make Assimp compile with GCC-4, a makefile and the xcode3 w - Andreas Nagel First Assimp testing & verification under Windows Vista 64 Bit. -- Marius Schröder +- Marius Schr�der Allowed us to use many of his models for screenshots and testing. - Christian Schubert @@ -92,7 +92,7 @@ Contributed the 'SimpleTexturedOpenGl' sample. - Matthias Fauconneau Contributed a fix for the Q3-BSP loader. -- Jørgen P. Tjernø +- J�rgen P. Tjern� Contributed updated and improved xcode workspaces - drparallax @@ -137,11 +137,14 @@ GCC/Linux fixes for the SimpleOpenGL sample. - Brian Miller Bugfix for a compiler fix for iOS on arm. -- Séverin Lemaignan +- S�verin Lemaignan Rewrite of PyAssimp, distutils and Python3 support - albert-wang Bugfixes for the collada parser - Ya ping Jin -Bugfixes for uv-tanget calculation. \ No newline at end of file +Bugfixes for uv-tanget calculation. + +- Jonne Nauha +Ogre Binary format support diff --git a/Readme.md b/Readme.md index 978584ab1..77306459c 100644 --- a/Readme.md +++ b/Readme.md @@ -1,12 +1,12 @@ Open Asset Import Library (assimp) ======== -Open Asset Import Library is a Open Source library designed to load various __3d file formats and convert them into a single, in-memory format__. It supports more than __30 file formats__ for import and a growing selection of file formats for export. Additionally, assimp features various __post processing tools__ to refine the imported data: _normals and tangent space generation, triangulation, vertex cache locality optimization, removal of degenerate primitives and duplicate vertices, sorting by primitive type, merging of redundant materials_ and many more. - -Its short name under which it is commonly known is __assimp__. +Open Asset Import Library is a Open Source library designed to load various __3d file formats and convert them into a shared, in-memory format__. It supports more than __30 file formats__ for import and a growing selection of file formats for export. Additionally, assimp features various __post processing tools__ to refine the imported data: _normals and tangent space generation, triangulation, vertex cache locality optimization, removal of degenerate primitives and duplicate vertices, sorting by primitive type, merging of redundant materials_ and many more. This is the development trunk of assimp containing the latest features and bugfixes. For productive use though, we recommend one of the stable releases available from [assimp.sf.net](http://assimp.sf.net) or from *nix package repositories. According to [Travis-CI] (https://travis-ci.org/), the current build status of the trunk is [![Build Status](https://travis-ci.org/assimp/assimp.png)](https://travis-ci.org/assimp/assimp) +[open3mod](https://github.com/acgessler/open3mod) is an Open Source 3D model viewer based off Assimp's import and export abilities. + #### Supported file formats #### The library provides importers for a lot of file formats, including: @@ -43,6 +43,7 @@ The library provides importers for a lot of file formats, including: - BVH - B3D - NDO +- Ogre Binary - Ogre XML - Q3D @@ -56,6 +57,7 @@ Exporters include: - STL - OBJ - PLY +- JSON (for WebGl, via https://github.com/acgessler/assimp2json) See [the full list here](http://assimp.sourceforge.net/main_features_formats.html). @@ -77,7 +79,7 @@ C++ish interface). The directory structure is: /scripts Scripts used to generate the loading code for some formats /port Ports to other languages and scripts to maintain those. /test Unit- and regression tests, test suite of models - /tools Tools (viewer, command line `assimp`) + /tools Tools (old assimp viewer, command line `assimp`) /samples A small number of samples to illustrate possible use cases for Assimp /workspaces Build enviroments for vc,xcode,... (deprecated, diff --git a/assimp-config.cmake.in b/assimp-config.cmake.in index 0cc380bd1..1b68b4f18 100644 --- a/assimp-config.cmake.in +++ b/assimp-config.cmake.in @@ -44,20 +44,20 @@ if (CMAKE_BUILD_TYPE EQUAL "DEBUG") set( ASSIMP_LIBRARIES ${ASSIMP_LIBRARIES}D) endif (CMAKE_BUILD_TYPE EQUAL "DEBUG") +# search for the boost version assimp was compiled with +#set(Boost_USE_MULTITHREAD ON) +#set(Boost_USE_STATIC_LIBS OFF) +#set(Boost_USE_STATIC_RUNTIME OFF) +#find_package(Boost ${ASSIMP_Boost_VERSION} EXACT COMPONENTS thread date_time) +#if(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0") +# set( ASSIMP_INCLUDE_DIRS "${ASSIMP_INCLUDE_DIRS}" ${Boost_INCLUDE_DIRS}) +#else(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0") +# message(WARNING "Failed to find Boost ${ASSIMP_Boost_VERSION} necessary for assimp") +#endif(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0") + # the boost version assimp was compiled with set( ASSIMP_Boost_VERSION "@Boost_MAJOR_VERSION@.@Boost_MINOR_VERSION@") -# search for the boost version assimp was compiled with -set(Boost_USE_MULTITHREAD ON) -set(Boost_USE_STATIC_LIBS OFF) -set(Boost_USE_STATIC_RUNTIME OFF) -find_package(Boost ${ASSIMP_Boost_VERSION} EXACT COMPONENTS thread date_time) -if(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0") - set( ASSIMP_INCLUDE_DIRS "${ASSIMP_INCLUDE_DIRS}" ${Boost_INCLUDE_DIRS}) -else(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0") - message(WARNING "Failed to find Boost ${ASSIMP_Boost_VERSION} necessary for assimp") -endif(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0") - # for compatibility wiht pkg-config set(ASSIMP_CFLAGS_OTHER "${ASSIMP_CXX_FLAGS}") set(ASSIMP_LDFLAGS_OTHER "${ASSIMP_LINK_FLAGS}") diff --git a/code/AssimpCExport.cpp b/code/AssimpCExport.cpp index 053e1b9f4..70a5177da 100644 --- a/code/AssimpCExport.cpp +++ b/code/AssimpCExport.cpp @@ -61,6 +61,8 @@ ASSIMP_API size_t aiGetExportFormatCount(void) // ------------------------------------------------------------------------------------------------ ASSIMP_API const aiExportFormatDesc* aiGetExportFormatDescription( size_t pIndex) { + // Note: this is valid as the index always pertains to a builtin exporter, + // for which the returned structure is guaranteed to be of static storage duration. return Exporter().GetExportFormatDescription(pIndex); } diff --git a/code/AssimpPCH.h b/code/AssimpPCH.h index c41915e9b..548d13175 100644 --- a/code/AssimpPCH.h +++ b/code/AssimpPCH.h @@ -56,7 +56,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Include our stdint.h replacement header for MSVC, take the global header for gcc/mingw #if defined( _MSC_VER) && (_MSC_VER < 1600) -# include "pstdint.h" +# include "../include/assimp/Compiler/pstdint.h" #else # include #endif diff --git a/code/BlenderModifier.cpp b/code/BlenderModifier.cpp index e2bea4d85..74410d0a0 100644 --- a/code/BlenderModifier.cpp +++ b/code/BlenderModifier.cpp @@ -248,6 +248,15 @@ void BlenderModifier_Mirror :: DoIt(aiNode& out, ConversionData& conv_data, co } } + // Only reverse the winding order if an odd number of axes were mirrored. + if (xs * ys * zs < 0) { + for( unsigned int i = 0; i < mesh->mNumFaces; i++) { + aiFace& face = mesh->mFaces[i]; + for( unsigned int fi = 0; fi < face.mNumIndices / 2; ++fi) + std::swap( face.mIndices[fi], face.mIndices[face.mNumIndices - 1 - fi]); + } + } + conv_data.meshes->push_back(mesh); } unsigned int* nind = new unsigned int[out.mNumMeshes*2]; diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index da39c10c4..0533a05fd 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -10,7 +10,7 @@ SET( HEADER_PATH ../include/assimp ) SET( COMPILER_HEADERS ${HEADER_PATH}/Compiler/pushpack1.h ${HEADER_PATH}/Compiler/poppack1.h - pstdint.h + ${HEADER_PATH}/Compiler/pstdint.h ) SOURCE_GROUP( Compiler FILES ${COMPILER_HEADERS}) @@ -187,7 +187,7 @@ SET( Collada_SRCS ColladaParser.cpp ColladaParser.h ColladaExporter.h - ColladaExporter.cpp + ColladaExporter.cpp ) SOURCE_GROUP( Collada FILES ${Collada_SRCS}) @@ -322,11 +322,15 @@ SOURCE_GROUP( Obj FILES ${Obj_SRCS}) SET( Ogre_SRCS OgreImporter.h + OgreStructs.h OgreParsingUtils.h + OgreBinarySerializer.h + OgreXmlSerializer.h OgreImporter.cpp + OgreStructs.cpp + OgreBinarySerializer.cpp + OgreXmlSerializer.cpp OgreMaterial.cpp - OgreMesh.cpp - OgreSkeleton.cpp ) SOURCE_GROUP( Ogre FILES ${Ogre_SRCS}) @@ -539,6 +543,8 @@ SET( XFile_SRCS XFileImporter.h XFileParser.cpp XFileParser.h + XFileExporter.h + XFileExporter.cpp ) SOURCE_GROUP( XFile FILES ${XFile_SRCS}) diff --git a/code/ColladaExporter.cpp b/code/ColladaExporter.cpp index cb392acf0..963827966 100644 --- a/code/ColladaExporter.cpp +++ b/code/ColladaExporter.cpp @@ -58,7 +58,7 @@ namespace Assimp // ------------------------------------------------------------------------------------------------ // Worker function for exporting a scene to Collada. Prototyped and registered in Exporter.cpp -void ExportSceneCollada(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene) +void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene) { std::string path = ""; std::string file = pFile; @@ -124,7 +124,7 @@ ColladaExporter::~ColladaExporter() void ColladaExporter::WriteFile() { // write the DTD - mOutput << "" << endstr; + mOutput << "" << endstr; // COLLADA element start mOutput << "" << endstr; PushTag(); @@ -151,7 +151,7 @@ void ColladaExporter::WriteFile() // Writes the asset header void ColladaExporter::WriteHeader() { - static const float epsilon = 0.000001f; + static const float epsilon = 0.00001f; static const aiQuaternion x_rot(aiMatrix3x3( 0, -1, 0, 1, 0, 0, @@ -176,6 +176,7 @@ void ColladaExporter::WriteHeader() aiQuaternion rotation; aiVector3D position; mScene->mRootNode->mTransformation.Decompose(scaling, rotation, position); + rotation.Normalize(); bool add_root_node = false; @@ -229,8 +230,22 @@ void ColladaExporter::WriteHeader() PushTag(); mOutput << startstr << "" << endstr; PushTag(); - mOutput << startstr << "Assimp" << endstr; - mOutput << startstr << "Assimp Collada Exporter" << endstr; + + aiMetadata* meta = mScene->mRootNode->mMetaData; + aiString value; + if (!meta || !meta->Get("Author", value)) + mOutput << startstr << "" << "Assimp" << "" << endstr; + else + mOutput << startstr << "" << value.C_Str() << "" << endstr; + + if (!meta || !meta->Get("AuthoringTool", value)) + mOutput << startstr << "" << "Assimp Exporter" << "" << endstr; + else + mOutput << startstr << "" << value.C_Str() << "" << endstr; + + //mOutput << startstr << "" << mScene->author.C_Str() << "" << endstr; + //mOutput << startstr << "" << mScene->authoringTool.C_Str() << "" << endstr; + PopTag(); mOutput << startstr << "" << endstr; mOutput << startstr << "" << date_str << "" << endstr; @@ -353,7 +368,8 @@ void ColladaExporter::WriteTextureColorEntry( const Surface& pSurface, const std if( pSurface.texture.empty() ) { mOutput << startstr << "" << pSurface.color.r << " " << pSurface.color.g << " " << pSurface.color.b << " " << pSurface.color.a << "" << endstr; - } else + } + else { mOutput << startstr << "" << endstr; } @@ -420,12 +436,12 @@ void ColladaExporter::WriteMaterials() aiString name; if( mat->Get( AI_MATKEY_NAME, name) != aiReturn_SUCCESS ) - name = "mat"; + name = "mat"; materials[a].name = std::string( "m") + boost::lexical_cast (a) + name.C_Str(); for( std::string::iterator it = materials[a].name.begin(); it != materials[a].name.end(); ++it ) { // isalnum on MSVC asserts for code points in [0,255]. Thus prevent unwanted promotion // of char to signed int and take the unsigned char value. - if( !isalnum( static_cast(*it) ) ) { + if( !isalnum( static_cast(*it) ) ) { *it = '_'; } } @@ -630,26 +646,63 @@ void ColladaExporter::WriteGeometry( size_t pIndex) PopTag(); mOutput << startstr << "" << endstr; - // write face setup - mOutput << startstr << "mNumFaces << "\" material=\"theresonlyone\">" << endstr; - PushTag(); - mOutput << startstr << "" << endstr; - - mOutput << startstr << ""; - for( size_t a = 0; a < mesh->mNumFaces; ++a ) - mOutput << mesh->mFaces[a].mNumIndices << " "; - mOutput << "" << endstr; - - mOutput << startstr << "

"; + // count the number of lines, triangles and polygon meshes + int countLines = 0; + int countPoly = 0; for( size_t a = 0; a < mesh->mNumFaces; ++a ) { - const aiFace& face = mesh->mFaces[a]; - for( size_t b = 0; b < face.mNumIndices; ++b ) - mOutput << face.mIndices[b] << " "; + if (mesh->mFaces[a].mNumIndices == 2) countLines++; + else if (mesh->mFaces[a].mNumIndices >= 3) countPoly++; + } + + // lines + if (countLines) + { + mOutput << startstr << "" << endstr; + PushTag(); + mOutput << startstr << "" << endstr; + mOutput << startstr << "

"; + for( size_t a = 0; a < mesh->mNumFaces; ++a ) + { + const aiFace& face = mesh->mFaces[a]; + if (face.mNumIndices != 2) continue; + for( size_t b = 0; b < face.mNumIndices; ++b ) + mOutput << face.mIndices[b] << " "; + } + mOutput << "

" << endstr; + PopTag(); + mOutput << startstr << "" << endstr; + } + + // triangle - dont use it, because compatibility problems + + // polygons + if (countPoly) + { + mOutput << startstr << "" << endstr; + PushTag(); + mOutput << startstr << "" << endstr; + + mOutput << startstr << ""; + for( size_t a = 0; a < mesh->mNumFaces; ++a ) + { + if (mesh->mFaces[a].mNumIndices < 3) continue; + mOutput << mesh->mFaces[a].mNumIndices << " "; + } + mOutput << "" << endstr; + + mOutput << startstr << "

"; + for( size_t a = 0; a < mesh->mNumFaces; ++a ) + { + const aiFace& face = mesh->mFaces[a]; + if (face.mNumIndices < 3) continue; + for( size_t b = 0; b < face.mNumIndices; ++b ) + mOutput << face.mIndices[b] << " "; + } + mOutput << "

" << endstr; + PopTag(); + mOutput << startstr << "
" << endstr; } - mOutput << "

" << endstr; - PopTag(); - mOutput << startstr << "
" << endstr; // closing tags PopTag(); @@ -770,8 +823,16 @@ void ColladaExporter::WriteSceneLibrary() // ------------------------------------------------------------------------------------------------ // Recursively writes the given node -void ColladaExporter::WriteNode( const aiNode* pNode) +void ColladaExporter::WriteNode(aiNode* pNode) { + // the must have a name + if (pNode->mName.length == 0) + { + std::stringstream ss; + ss << "Node_" << pNode; + pNode->mName.Set(ss.str()); + } + mOutput << startstr << "mName.data << "\" name=\"" << pNode->mName.data << "\">" << endstr; PushTag(); @@ -799,7 +860,7 @@ void ColladaExporter::WriteNode( const aiNode* pNode) PushTag(); mOutput << startstr << "" << endstr; PushTag(); - mOutput << startstr << "mMaterialIndex].name << "\" />" << endstr; + mOutput << startstr << "mMaterialIndex].name << "\" />" << endstr; PopTag(); mOutput << startstr << "" << endstr; PopTag(); diff --git a/code/ColladaExporter.h b/code/ColladaExporter.h index f96671c40..47b8405e0 100644 --- a/code/ColladaExporter.h +++ b/code/ColladaExporter.h @@ -92,7 +92,7 @@ protected: void WriteSceneLibrary(); /// Recursively writes the given node - void WriteNode( const aiNode* pNode); + void WriteNode( aiNode* pNode); /// Enters a new xml element, which increases the indentation void PushTag() { startstr.append( " "); } diff --git a/code/ColladaLoader.cpp b/code/ColladaLoader.cpp index 7e4704b8f..e721c99b8 100644 --- a/code/ColladaLoader.cpp +++ b/code/ColladaLoader.cpp @@ -133,6 +133,7 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I mLights.clear(); mCameras.clear(); mTextures.clear(); + mAnims.clear(); // parse the input file ColladaParser parser( pIOHandler, pFile); @@ -904,6 +905,8 @@ void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pPars pScene->mAnimations = new aiAnimation*[mAnims.size()]; std::copy( mAnims.begin(), mAnims.end(), pScene->mAnimations); } + + mAnims.clear(); } // ------------------------------------------------------------------------------------------------ diff --git a/code/ConvertToLHProcess.cpp b/code/ConvertToLHProcess.cpp index cc9141306..3a51daf57 100644 --- a/code/ConvertToLHProcess.cpp +++ b/code/ConvertToLHProcess.cpp @@ -134,7 +134,7 @@ void MakeLeftHandedProcess::ProcessMesh( aiMesh* pMesh) { pMesh->mVertices[a].z *= -1.0f; if( pMesh->HasNormals()) - pMesh->mNormals[a].z *= -1.0f; + pMesh->mNormals[a].z *= -1.0f; if( pMesh->HasTangentsAndBitangents()) { pMesh->mTangents[a].z *= -1.0f; diff --git a/code/Exporter.cpp b/code/Exporter.cpp index a693f75e2..18bf77e4a 100644 --- a/code/Exporter.cpp +++ b/code/Exporter.cpp @@ -71,7 +71,9 @@ void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out); // ------------------------------------------------------------------------------------------------ // Exporter worker function prototypes. Should not be necessary to #ifndef them, it's just a prototype +// do not use const, because some exporter need to convert the scene temporary void ExportSceneCollada(const char*,IOSystem*, const aiScene*); +void ExportSceneXFile(const char*,IOSystem*, const aiScene*); void ExportSceneObj(const char*,IOSystem*, const aiScene*); void ExportSceneSTL(const char*,IOSystem*, const aiScene*); void ExportSceneSTLBinary(const char*,IOSystem*, const aiScene*); @@ -86,6 +88,11 @@ Exporter::ExportFormatEntry gExporters[] = Exporter::ExportFormatEntry( "collada", "COLLADA - Digital Asset Exchange Schema", "dae", &ExportSceneCollada), #endif +#ifndef ASSIMP_BUILD_NO_FXILE_EXPORTER + Exporter::ExportFormatEntry( "x", "X Files", "x", &ExportSceneXFile, + aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_FlipUVs), +#endif + #ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER Exporter::ExportFormatEntry( "obj", "Wavefront OBJ format", "obj", &ExportSceneObj, aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */), @@ -436,6 +443,11 @@ const aiExportFormatDesc* Exporter :: GetExportFormatDescription( size_t pIndex if (pIndex >= GetExportFormatCount()) { return NULL; } + + // Return from static storage if the requested index is built-in. + if (pIndex < sizeof(gExporters) / sizeof(gExporters[0])) { + return &gExporters[pIndex].mDescription; + } return &pimpl->mExporters[pIndex].mDescription; } diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index f623c3ae7..075a4903c 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -121,7 +121,7 @@ public: if(mat) { if (materials_converted.find(mat) == materials_converted.end()) { - ConvertMaterial(*mat); + ConvertMaterial(*mat, 0); } } } @@ -767,7 +767,6 @@ private: // find user defined properties (3ds Max) data->Set(index++, "UserProperties", aiString(PropertyGet(props, "UDP3DSMAX", ""))); - unparsedProperties.erase("UDP3DSMAX"); // preserve the info that a node was marked as Null node in the original file. data->Set(index++, "IsNull", model.IsNull() ? true : false); @@ -783,8 +782,8 @@ private: data->Set(index++, prop.first, interpreted->Value()); else if (const TypedProperty* interpreted = prop.second->As >()) data->Set(index++, prop.first, interpreted->Value()); - else if (const TypedProperty* interpreted = prop.second->As >()) - data->Set(index++, prop.first, interpreted->Value()); + else if (const TypedProperty* interpreted = prop.second->As >()) + data->Set(index++, prop.first, aiString(interpreted->Value())); else if (const TypedProperty* interpreted = prop.second->As >()) data->Set(index++, prop.first, interpreted->Value()); else @@ -1381,7 +1380,7 @@ private: return; } - out->mMaterialIndex = ConvertMaterial(*mat); + out->mMaterialIndex = ConvertMaterial(*mat, &geo); materials_converted[mat] = out->mMaterialIndex; } @@ -1411,7 +1410,7 @@ private: // ------------------------------------------------------------------------------------------------ // Material -> aiMaterial - unsigned int ConvertMaterial(const Material& material) + unsigned int ConvertMaterial(const Material& material, const MeshGeometry* const mesh) { const PropertyTable& props = material.Props(); @@ -1440,8 +1439,8 @@ private: SetShadingPropertiesCommon(out_mat,props); // texture assignments - SetTextureProperties(out_mat,material.Textures()); - SetTextureProperties(out_mat,material.LayeredTextures()); + SetTextureProperties(out_mat,material.Textures(), mesh); + SetTextureProperties(out_mat,material.LayeredTextures(), mesh); return static_cast(materials.size() - 1); } @@ -1450,7 +1449,7 @@ private: // ------------------------------------------------------------------------------------------------ void TrySetTextureProperties(aiMaterial* out_mat, const TextureMap& textures, const std::string& propName, - aiTextureType target) + aiTextureType target, const MeshGeometry* const mesh) { TextureMap::const_iterator it = textures.find(propName); if(it == textures.end()) { @@ -1495,18 +1494,48 @@ private: std::find(materials.begin(),materials.end(),out_mat) )); - uvIndex = -1; - BOOST_FOREACH(const MeshMap::value_type& v,meshes_converted) { - const MeshGeometry* const mesh = dynamic_cast (v.first); - if(!mesh) { - continue; - } - const MatIndexArray& mats = mesh->GetMaterialIndices(); - if(std::find(mats.begin(),mats.end(),matIndex) == mats.end()) { - continue; - } + uvIndex = -1; + if (!mesh) + { + BOOST_FOREACH(const MeshMap::value_type& v,meshes_converted) { + const MeshGeometry* const mesh = dynamic_cast (v.first); + if(!mesh) { + continue; + } + const MatIndexArray& mats = mesh->GetMaterialIndices(); + if(std::find(mats.begin(),mats.end(),matIndex) == mats.end()) { + continue; + } + + int index = -1; + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + if(mesh->GetTextureCoords(i).empty()) { + break; + } + const std::string& name = mesh->GetTextureCoordChannelName(i); + if(name == uvSet) { + index = static_cast(i); + break; + } + } + if(index == -1) { + FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); + continue; + } + + if(uvIndex == -1) { + uvIndex = index; + } + else { + FBXImporter::LogWarn("the UV channel named " + uvSet + + " appears at different positions in meshes, results will be wrong"); + } + } + } + else + { int index = -1; for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { if(mesh->GetTextureCoords(i).empty()) { @@ -1520,17 +1549,12 @@ private: } if(index == -1) { FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); - continue; } if(uvIndex == -1) { uvIndex = index; } - else { - FBXImporter::LogWarn("the UV channel named " + uvSet + - " 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"); @@ -1546,7 +1570,7 @@ private: // ------------------------------------------------------------------------------------------------ void TrySetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, const std::string& propName, - aiTextureType target) + aiTextureType target, const MeshGeometry* const mesh) { LayeredTextureMap::const_iterator it = layeredTextures.find(propName); if(it == layeredTextures.end()) { @@ -1590,18 +1614,47 @@ private: std::find(materials.begin(),materials.end(),out_mat) )); - uvIndex = -1; - BOOST_FOREACH(const MeshMap::value_type& v,meshes_converted) { - const MeshGeometry* const mesh = dynamic_cast (v.first); - if(!mesh) { - continue; - } + uvIndex = -1; + if (!mesh) + { + BOOST_FOREACH(const MeshMap::value_type& v,meshes_converted) { + const MeshGeometry* const mesh = dynamic_cast (v.first); + if(!mesh) { + continue; + } - const MatIndexArray& mats = mesh->GetMaterialIndices(); - if(std::find(mats.begin(),mats.end(),matIndex) == mats.end()) { - continue; - } + const MatIndexArray& mats = mesh->GetMaterialIndices(); + if(std::find(mats.begin(),mats.end(),matIndex) == mats.end()) { + continue; + } + int index = -1; + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + if(mesh->GetTextureCoords(i).empty()) { + break; + } + const std::string& name = mesh->GetTextureCoordChannelName(i); + if(name == uvSet) { + index = static_cast(i); + break; + } + } + if(index == -1) { + FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); + continue; + } + + if(uvIndex == -1) { + uvIndex = index; + } + else { + FBXImporter::LogWarn("the UV channel named " + uvSet + + " appears at different positions in meshes, results will be wrong"); + } + } + } + else + { int index = -1; for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { if(mesh->GetTextureCoords(i).empty()) { @@ -1615,17 +1668,12 @@ private: } if(index == -1) { FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); - continue; } if(uvIndex == -1) { uvIndex = index; } - else { - FBXImporter::LogWarn("the UV channel named " + uvSet + - " 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"); @@ -1638,33 +1686,33 @@ private: } // ------------------------------------------------------------------------------------------------ - void SetTextureProperties(aiMaterial* out_mat, const TextureMap& textures) + void SetTextureProperties(aiMaterial* out_mat, const TextureMap& textures, const MeshGeometry* const mesh) { - TrySetTextureProperties(out_mat, textures, "DiffuseColor", aiTextureType_DIFFUSE); - TrySetTextureProperties(out_mat, textures, "AmbientColor", aiTextureType_AMBIENT); - TrySetTextureProperties(out_mat, textures, "EmissiveColor", aiTextureType_EMISSIVE); - TrySetTextureProperties(out_mat, textures, "SpecularColor", aiTextureType_SPECULAR); - TrySetTextureProperties(out_mat, textures, "TransparentColor", aiTextureType_OPACITY); - TrySetTextureProperties(out_mat, textures, "ReflectionColor", aiTextureType_REFLECTION); - TrySetTextureProperties(out_mat, textures, "DisplacementColor", aiTextureType_DISPLACEMENT); - TrySetTextureProperties(out_mat, textures, "NormalMap", aiTextureType_NORMALS); - TrySetTextureProperties(out_mat, textures, "Bump", aiTextureType_HEIGHT); - TrySetTextureProperties(out_mat, textures, "ShininessExponent", aiTextureType_SHININESS); + TrySetTextureProperties(out_mat, textures, "DiffuseColor", aiTextureType_DIFFUSE, mesh); + TrySetTextureProperties(out_mat, textures, "AmbientColor", aiTextureType_AMBIENT, mesh); + TrySetTextureProperties(out_mat, textures, "EmissiveColor", aiTextureType_EMISSIVE, mesh); + TrySetTextureProperties(out_mat, textures, "SpecularColor", aiTextureType_SPECULAR, mesh); + TrySetTextureProperties(out_mat, textures, "TransparentColor", aiTextureType_OPACITY, mesh); + TrySetTextureProperties(out_mat, textures, "ReflectionColor", aiTextureType_REFLECTION, mesh); + TrySetTextureProperties(out_mat, textures, "DisplacementColor", aiTextureType_DISPLACEMENT, mesh); + TrySetTextureProperties(out_mat, textures, "NormalMap", aiTextureType_NORMALS, mesh); + TrySetTextureProperties(out_mat, textures, "Bump", aiTextureType_HEIGHT, mesh); + TrySetTextureProperties(out_mat, textures, "ShininessExponent", aiTextureType_SHININESS, mesh); } // ------------------------------------------------------------------------------------------------ - void SetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures) + void SetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, const MeshGeometry* const mesh) { - TrySetTextureProperties(out_mat, layeredTextures, "DiffuseColor", aiTextureType_DIFFUSE); - TrySetTextureProperties(out_mat, layeredTextures, "AmbientColor", aiTextureType_AMBIENT); - TrySetTextureProperties(out_mat, layeredTextures, "EmissiveColor", aiTextureType_EMISSIVE); - TrySetTextureProperties(out_mat, layeredTextures, "SpecularColor", aiTextureType_SPECULAR); - TrySetTextureProperties(out_mat, layeredTextures, "TransparentColor", aiTextureType_OPACITY); - TrySetTextureProperties(out_mat, layeredTextures, "ReflectionColor", aiTextureType_REFLECTION); - TrySetTextureProperties(out_mat, layeredTextures, "DisplacementColor", aiTextureType_DISPLACEMENT); - TrySetTextureProperties(out_mat, layeredTextures, "NormalMap", aiTextureType_NORMALS); - TrySetTextureProperties(out_mat, layeredTextures, "Bump", aiTextureType_HEIGHT); - TrySetTextureProperties(out_mat, layeredTextures, "ShininessExponent", aiTextureType_SHININESS); + TrySetTextureProperties(out_mat, layeredTextures, "DiffuseColor", aiTextureType_DIFFUSE, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "AmbientColor", aiTextureType_AMBIENT, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "EmissiveColor", aiTextureType_EMISSIVE, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "SpecularColor", aiTextureType_SPECULAR, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "TransparentColor", aiTextureType_OPACITY, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "ReflectionColor", aiTextureType_REFLECTION, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "DisplacementColor", aiTextureType_DISPLACEMENT, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "NormalMap", aiTextureType_NORMALS, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "Bump", aiTextureType_HEIGHT, mesh); + TrySetTextureProperties(out_mat, layeredTextures, "ShininessExponent", aiTextureType_SHININESS, mesh); } diff --git a/code/FBXImportSettings.h b/code/FBXImportSettings.h index 47fc1d6a6..2020273d7 100644 --- a/code/FBXImportSettings.h +++ b/code/FBXImportSettings.h @@ -53,7 +53,7 @@ struct ImportSettings ImportSettings() : strictMode(true) , readAllLayers(true) - , readAllMaterials() + , readAllMaterials(false) , readMaterials(true) , readCameras(true) , readLights(true) diff --git a/code/FBXParser.cpp b/code/FBXParser.cpp index fce1143fd..f6dc2e7e2 100644 --- a/code/FBXParser.cpp +++ b/code/FBXParser.cpp @@ -364,14 +364,24 @@ float ParseTokenAsFloat(const Token& t, const char*& err_out) } if (data[0] == 'F') { - ai_assert(t.end() - data == 5); - // no byte swapping needed for ieee floats - return *reinterpret_cast(data+1); + // Actual size validation happens during Tokenization so + // this is valid as an assertion. + ai_assert(t.end() - data == sizeof(float) + 1); + // Initially, we did reinterpret_cast, breaking strict aliasing rules. + // This actually caused trouble on Android, so let's be safe this time. + // https://github.com/assimp/assimp/issues/24 + + float out_float; + ::memcpy(&out_float, data+1, sizeof(float)); + return out_float; } else { - ai_assert(t.end() - data == 9); - // no byte swapping needed for ieee floats - return static_cast(*reinterpret_cast(data+1)); + ai_assert(t.end() - data == sizeof(double) + 1); + + // Same + double out_double; + ::memcpy(&out_double, data+1, sizeof(double)); + return static_cast(out_double); } } diff --git a/code/ObjExporter.cpp b/code/ObjExporter.cpp index ff9fa4b25..282983965 100644 --- a/code/ObjExporter.cpp +++ b/code/ObjExporter.cpp @@ -199,7 +199,7 @@ void ObjExporter :: WriteGeometryFile() // collect mesh geometry aiMatrix4x4 mBase; - AddNode(pScene->mRootNode,mBase); + AddNode(pScene->mRootNode, mBase); // write vertex positions vpMap.getVectors(vp); @@ -228,7 +228,9 @@ void ObjExporter :: WriteGeometryFile() // now write all mesh instances BOOST_FOREACH(const MeshInstance& m, meshes) { mOutput << "# Mesh \'" << m.name << "\' with " << m.faces.size() << " faces" << endl; - mOutput << "g " << m.name << endl; + if (!m.name.empty()) { + mOutput << "g " << m.name << endl; + } mOutput << "usemtl " << m.matname << endl; BOOST_FOREACH(const Face& f, m.faces) { @@ -243,11 +245,8 @@ void ObjExporter :: WriteGeometryFile() if (fv.vt) { mOutput << fv.vt; } - if (f.kind == 'f') { - mOutput << '/'; - if (fv.vn) { - mOutput << fv.vn; - } + if (f.kind == 'f' && fv.vn) { + mOutput << '/' << fv.vn; } } } @@ -258,14 +257,12 @@ void ObjExporter :: WriteGeometryFile() } } - - - - +// ------------------------------------------------------------------------------------------------ int ObjExporter::vecIndexMap::getIndex(const aiVector3D& vec) { - vecIndexMap::dataType::iterator vertIt = vecMap.find(vec); - if(vertIt != vecMap.end()){// vertex already exists, so reference it + vecIndexMap::dataType::iterator vertIt = vecMap.find(vec); + // vertex already exists, so reference it + if(vertIt != vecMap.end()){ return vertIt->second; } vecMap[vec] = mNextIndex; @@ -274,6 +271,7 @@ int ObjExporter::vecIndexMap::getIndex(const aiVector3D& vec) return ret; } +// ------------------------------------------------------------------------------------------------ void ObjExporter::vecIndexMap::getVectors( std::vector& vecs ) { vecs.resize(vecMap.size()); @@ -282,14 +280,13 @@ void ObjExporter::vecIndexMap::getVectors( std::vector& vecs ) } } - // ------------------------------------------------------------------------------------------------ void ObjExporter :: AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4x4& mat) { meshes.push_back(MeshInstance()); MeshInstance& mesh = meshes.back(); - mesh.name = std::string(name.data,name.length) + (m->mName.length ? "_"+std::string(m->mName.data,m->mName.length) : ""); + mesh.name = std::string(name.data,name.length) + (m->mName.length ? "_" + std::string(m->mName.data,m->mName.length) : ""); mesh.matname = GetMaterialName(m->mMaterialIndex); mesh.faces.resize(m->mNumFaces); @@ -317,7 +314,8 @@ void ObjExporter :: AddMesh(const aiString& name, const aiMesh* m, const aiMatri face.indices[a].vp = vpMap.getIndex(vert); if (m->mNormals) { - face.indices[a].vn = vnMap.getIndex(m->mNormals[idx]); + aiVector3D norm = aiMatrix3x3(mat) * m->mNormals[idx]; + face.indices[a].vn = vnMap.getIndex(norm); } else{ face.indices[a].vn = 0; @@ -339,11 +337,11 @@ void ObjExporter :: AddNode(const aiNode* nd, const aiMatrix4x4& mParent) const aiMatrix4x4& mAbs = mParent * nd->mTransformation; for(unsigned int i = 0; i < nd->mNumMeshes; ++i) { - AddMesh(nd->mName, pScene->mMeshes[nd->mMeshes[i]],mAbs); + AddMesh(nd->mName, pScene->mMeshes[nd->mMeshes[i]], mAbs); } for(unsigned int i = 0; i < nd->mNumChildren; ++i) { - AddNode(nd->mChildren[i],mAbs); + AddNode(nd->mChildren[i], mAbs); } } diff --git a/code/ObjExporter.h b/code/ObjExporter.h index 4f6555aa5..e1ec7a9a6 100644 --- a/code/ObjExporter.h +++ b/code/ObjExporter.h @@ -38,7 +38,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ -/** @file ColladaExporter.h +/** @file ObjExporter.h * Declares the exporter class to write a scene to a Collada file */ #ifndef AI_OBJEXPORTER_H_INC diff --git a/code/ObjFileImporter.cpp b/code/ObjFileImporter.cpp index ab5d4c351..65a8be314 100644 --- a/code/ObjFileImporter.cpp +++ b/code/ObjFileImporter.cpp @@ -139,7 +139,23 @@ void ObjFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, { strModelName = pFile; } - + + // process all '\' + std::vector ::iterator iter = m_Buffer.begin(); + while (iter != m_Buffer.end()) + { + if (*iter == '\\') + { + // remove '\' + iter = m_Buffer.erase(iter); + // remove next character + while (*iter == '\r' || *iter == '\n') + iter = m_Buffer.erase(iter); + } + else + ++iter; + } + // parse the file into a temporary representation ObjFileParser parser(m_Buffer, strModelName, pIOHandler); diff --git a/code/OgreBinarySerializer.cpp b/code/OgreBinarySerializer.cpp new file mode 100644 index 000000000..6674f4cee --- /dev/null +++ b/code/OgreBinarySerializer.cpp @@ -0,0 +1,1110 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#include "OgreBinarySerializer.h" +#include "OgreXmlSerializer.h" +#include "OgreParsingUtils.h" + +#include "TinyFormatter.h" + +#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER + +// Define as 1 to get verbose logging. +#define OGRE_BINARY_SERIALIZER_DEBUG 0 + +namespace Assimp +{ +namespace Ogre +{ + +const std::string MESH_VERSION_1_8 = "[MeshSerializer_v1.8]"; +const std::string SKELETON_VERSION_1_8 = "[Serializer_v1.80]"; +const std::string SKELETON_VERSION_1_1 = "[Serializer_v1.10]"; + +const unsigned short HEADER_CHUNK_ID = 0x1000; + +const long MSTREAM_OVERHEAD_SIZE = sizeof(uint16_t) + sizeof(uint32_t); +const long MSTREAM_BONE_SIZE_WITHOUT_SCALE = MSTREAM_OVERHEAD_SIZE + sizeof(unsigned short) + (sizeof(float) * 7); +const long MSTREAM_KEYFRAME_SIZE_WITHOUT_SCALE = MSTREAM_OVERHEAD_SIZE + (sizeof(float) * 8); + +template<> +inline bool OgreBinarySerializer::Read() +{ + return (m_reader->GetU1() > 0); +} + +template<> +inline char OgreBinarySerializer::Read() +{ + return static_cast(m_reader->GetU1()); +} + +template<> +inline uint8_t OgreBinarySerializer::Read() +{ + return m_reader->GetU1(); +} + +template<> +inline uint16_t OgreBinarySerializer::Read() +{ + return m_reader->GetU2(); +} + +template<> +inline uint32_t OgreBinarySerializer::Read() +{ + return m_reader->GetU4(); +} + +template<> +inline float OgreBinarySerializer::Read() +{ + return m_reader->GetF4(); +} + +void OgreBinarySerializer::ReadBytes(char *dest, size_t numBytes) +{ + ReadBytes(static_cast(dest), numBytes); +} + +void OgreBinarySerializer::ReadBytes(uint8_t *dest, size_t numBytes) +{ + ReadBytes(static_cast(dest), numBytes); +} + +void OgreBinarySerializer::ReadBytes(void *dest, size_t numBytes) +{ + m_reader->CopyAndAdvance(dest, numBytes); +} + +uint8_t *OgreBinarySerializer::ReadBytes(size_t numBytes) +{ + uint8_t *bytes = new uint8_t[numBytes]; + ReadBytes(bytes, numBytes); + return bytes; +} + +void OgreBinarySerializer::ReadVector(aiVector3D &vec) +{ + m_reader->CopyAndAdvance(&vec.x, sizeof(float)*3); +} + +void OgreBinarySerializer::ReadQuaternion(aiQuaternion &quat) +{ + float temp[4]; + m_reader->CopyAndAdvance(temp, sizeof(float)*4); + quat.x = temp[0]; + quat.y = temp[1]; + quat.z = temp[2]; + quat.w = temp[3]; +} + +bool OgreBinarySerializer::AtEnd() const +{ + return (m_reader->GetRemainingSize() == 0); +} + +std::string OgreBinarySerializer::ReadString(size_t len) +{ + std::string str; + str.resize(len); + ReadBytes(&str[0], len); + return str; +} + +std::string OgreBinarySerializer::ReadLine() +{ + std::string str; + while(!AtEnd()) + { + char c = Read(); + if (c == '\n') + break; + str += c; + } + return str; +} + +uint16_t OgreBinarySerializer::ReadHeader(bool readLen) +{ + uint16_t id = Read(); + if (readLen) + m_currentLen = Read(); + +#if (OGRE_BINARY_SERIALIZER_DEBUG == 1) + if (id != HEADER_CHUNK_ID) + { + DefaultLogger::get()->debug(Formatter::format() << (assetMode == AM_Mesh + ? MeshHeaderToString(static_cast(id)) : SkeletonHeaderToString(static_cast(id)))); + } +#endif + + return id; +} + +void OgreBinarySerializer::RollbackHeader() +{ + m_reader->IncPtr(-MSTREAM_OVERHEAD_SIZE); +} + +void OgreBinarySerializer::SkipBytes(size_t numBytes) +{ +#if (OGRE_BINARY_SERIALIZER_DEBUG == 1) + DefaultLogger::get()->debug(Formatter::format() << "Skipping " << numBytes << " bytes"); +#endif + + m_reader->IncPtr(numBytes); +} + +// Mesh + +Mesh *OgreBinarySerializer::ImportMesh(MemoryStreamReader *stream) +{ + OgreBinarySerializer serializer(stream, OgreBinarySerializer::AM_Mesh); + + uint16_t id = serializer.ReadHeader(false); + if (id != HEADER_CHUNK_ID) { + throw DeadlyExportError("Invalid Ogre Mesh file header."); + } + + /// @todo Check what we can actually support. + std::string version = serializer.ReadLine(); + if (version != MESH_VERSION_1_8) + { + throw DeadlyExportError(Formatter::format() << "Mesh version " << version << " not supported by this importer. Run OgreMeshUpgrader tool on the file and try again." + << " Supported versions: " << MESH_VERSION_1_8); + } + + Mesh *mesh = new Mesh(); + while (!serializer.AtEnd()) + { + id = serializer.ReadHeader(); + switch(id) + { + case M_MESH: + { + serializer.ReadMesh(mesh); + break; + } + } + } + return mesh; +} + +void OgreBinarySerializer::ReadMesh(Mesh *mesh) +{ + mesh->hasSkeletalAnimations = Read(); + + DefaultLogger::get()->debug("Reading Mesh"); + DefaultLogger::get()->debug(Formatter::format() << " - Skeletal animations: " << (mesh->hasSkeletalAnimations ? "true" : "false")); + + if (!AtEnd()) + { + uint16_t id = ReadHeader(); + while (!AtEnd() && + (id == M_GEOMETRY || + id == M_SUBMESH || + id == M_MESH_SKELETON_LINK || + id == M_MESH_BONE_ASSIGNMENT || + id == M_MESH_LOD || + id == M_MESH_BOUNDS || + id == M_SUBMESH_NAME_TABLE || + id == M_EDGE_LISTS || + id == M_POSES || + id == M_ANIMATIONS || + id == M_TABLE_EXTREMES)) + { + switch(id) + { + case M_GEOMETRY: + { + mesh->sharedVertexData = new VertexData(); + ReadGeometry(mesh->sharedVertexData); + break; + } + case M_SUBMESH: + { + ReadSubMesh(mesh); + break; + } + case M_MESH_SKELETON_LINK: + { + ReadMeshSkeletonLink(mesh); + break; + } + case M_MESH_BONE_ASSIGNMENT: + { + ReadBoneAssignment(mesh->sharedVertexData); + break; + } + case M_MESH_LOD: + { + ReadMeshLodInfo(mesh); + break; + } + case M_MESH_BOUNDS: + { + ReadMeshBounds(mesh); + break; + } + case M_SUBMESH_NAME_TABLE: + { + ReadSubMeshNames(mesh); + break; + } + case M_EDGE_LISTS: + { + ReadEdgeList(mesh); + break; + } + case M_POSES: + { + ReadPoses(mesh); + break; + } + case M_ANIMATIONS: + { + ReadAnimations(mesh); + break; + } + case M_TABLE_EXTREMES: + { + ReadMeshExtremes(mesh); + break; + } + } + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } + + NormalizeBoneWeights(mesh->sharedVertexData); +} + +void OgreBinarySerializer::ReadMeshLodInfo(Mesh *mesh) +{ + // Assimp does not acknowledge LOD levels as far as I can see it. This info is just skipped. + // @todo Put this stuff to scene/mesh custom properties. If manual mesh the app can use the information. + ReadLine(); // strategy name + uint16_t numLods = Read(); + bool manual = Read(); + + /// @note Main mesh is considered as LOD 0, start from index 1. + for (size_t i=1; iIncPtr(sizeof(float)); // user value + + if (manual) + { + id = ReadHeader(); + if (id != M_MESH_LOD_MANUAL) { + throw DeadlyImportError("Manual M_MESH_LOD_USAGE does not contain M_MESH_LOD_MANUAL"); + } + + ReadLine(); // manual mesh name (ref to another mesh) + } + else + { + for(size_t si=0, silen=mesh->NumSubMeshes(); si(); + bool is32bit = Read(); + + if (indexCount > 0) + { + uint32_t len = indexCount * (is32bit ? sizeof(uint32_t) : sizeof(uint16_t)); + m_reader->IncPtr(len); + } + } + } + } +} + +void OgreBinarySerializer::ReadMeshSkeletonLink(Mesh *mesh) +{ + mesh->skeletonRef = ReadLine(); +} + +void OgreBinarySerializer::ReadMeshBounds(Mesh *mesh) +{ + // Skip bounds, not compatible with Assimp. + // 2x float vec3 + 1x float sphere radius + SkipBytes(sizeof(float) * 7); +} + +void OgreBinarySerializer::ReadMeshExtremes(Mesh *mesh) +{ + // Skip extremes, not compatible with Assimp. + size_t numBytes = m_currentLen - MSTREAM_OVERHEAD_SIZE; + SkipBytes(numBytes); +} + +void OgreBinarySerializer::ReadBoneAssignment(VertexData *dest) +{ + if (!dest) { + throw DeadlyImportError("Cannot read bone assignments, vertex data is null."); + } + + VertexBoneAssignment ba; + ba.vertexIndex = Read(); + ba.boneIndex = Read(); + ba.weight = Read(); + + dest->boneAssignments.push_back(ba); +} + +void OgreBinarySerializer::ReadSubMesh(Mesh *mesh) +{ + uint16_t id = 0; + + SubMesh *submesh = new SubMesh(); + submesh->materialRef = ReadLine(); + submesh->usesSharedVertexData = Read(); + + submesh->indexData->count = Read(); + submesh->indexData->faceCount = static_cast(submesh->indexData->count / 3); + submesh->indexData->is32bit = Read(); + + DefaultLogger::get()->debug(Formatter::format() << "Reading SubMesh " << mesh->subMeshes.size()); + DefaultLogger::get()->debug(Formatter::format() << " - Material: '" << submesh->materialRef << "'"); + DefaultLogger::get()->debug(Formatter::format() << " - Uses shared geometry: " << (submesh->usesSharedVertexData ? "true" : "false")); + + // Index buffer + if (submesh->indexData->count > 0) + { + uint32_t numBytes = submesh->indexData->count * (submesh->indexData->is32bit ? sizeof(uint32_t) : sizeof(uint16_t)); + uint8_t *indexBuffer = ReadBytes(numBytes); + submesh->indexData->buffer = MemoryStreamPtr(new Assimp::MemoryIOStream(indexBuffer, numBytes, true)); + + DefaultLogger::get()->debug(Formatter::format() << " - " << submesh->indexData->faceCount + << " faces from " << submesh->indexData->count << (submesh->indexData->is32bit ? " 32bit" : " 16bit") + << " indexes of " << numBytes << " bytes"); + } + + // Vertex buffer if not referencing the shared geometry + if (!submesh->usesSharedVertexData) + { + id = ReadHeader(); + if (id != M_GEOMETRY) { + throw DeadlyImportError("M_SUBMESH does not contain M_GEOMETRY, but shader geometry is set to false"); + } + + submesh->vertexData = new VertexData(); + ReadGeometry(submesh->vertexData); + } + + // Bone assignment, submesh operation and texture aliases + if (!AtEnd()) + { + id = ReadHeader(); + while (!AtEnd() && + (id == M_SUBMESH_OPERATION || + id == M_SUBMESH_BONE_ASSIGNMENT || + id == M_SUBMESH_TEXTURE_ALIAS)) + { + switch(id) + { + case M_SUBMESH_OPERATION: + { + ReadSubMeshOperation(submesh); + break; + } + case M_SUBMESH_BONE_ASSIGNMENT: + { + ReadBoneAssignment(submesh->vertexData); + break; + } + case M_SUBMESH_TEXTURE_ALIAS: + { + ReadSubMeshTextureAlias(submesh); + break; + } + } + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } + + NormalizeBoneWeights(submesh->vertexData); + + submesh->index = mesh->subMeshes.size(); + mesh->subMeshes.push_back(submesh); +} + +void OgreBinarySerializer::NormalizeBoneWeights(VertexData *vertexData) const +{ + if (!vertexData || vertexData->boneAssignments.empty()) + return; + + std::set influencedVertices; + for (VertexBoneAssignmentList::const_iterator baIter=vertexData->boneAssignments.begin(), baEnd=vertexData->boneAssignments.end(); baIter != baEnd; ++baIter) { + influencedVertices.insert(baIter->vertexIndex); + } + + /** Normalize bone weights. + Some exporters wont care if the sum of all bone weights + for a single vertex equals 1 or not, so validate here. */ + const float epsilon = 0.05f; + for(std::set::const_iterator iter=influencedVertices.begin(), end=influencedVertices.end(); iter != end; ++iter) + { + const uint32_t vertexIndex = (*iter); + + float sum = 0.0f; + for (VertexBoneAssignmentList::const_iterator baIter=vertexData->boneAssignments.begin(), baEnd=vertexData->boneAssignments.end(); baIter != baEnd; ++baIter) + { + if (baIter->vertexIndex == vertexIndex) + sum += baIter->weight; + } + if ((sum < (1.0f - epsilon)) || (sum > (1.0f + epsilon))) + { + for (VertexBoneAssignmentList::iterator baIter=vertexData->boneAssignments.begin(), baEnd=vertexData->boneAssignments.end(); baIter != baEnd; ++baIter) + { + if (baIter->vertexIndex == vertexIndex) + baIter->weight /= sum; + } + } + } +} + +void OgreBinarySerializer::ReadSubMeshOperation(SubMesh *submesh) +{ + submesh->operationType = static_cast(Read()); +} + +void OgreBinarySerializer::ReadSubMeshTextureAlias(SubMesh *submesh) +{ + submesh->textureAliasName = ReadLine(); + submesh->textureAliasRef = ReadLine(); +} + +void OgreBinarySerializer::ReadSubMeshNames(Mesh *mesh) +{ + uint16_t id = 0; + uint16_t submeshIndex = 0; + + if (!AtEnd()) + { + id = ReadHeader(); + while (!AtEnd() && id == M_SUBMESH_NAME_TABLE_ELEMENT) + { + uint16_t submeshIndex = Read(); + SubMesh *submesh = mesh->GetSubMesh(submeshIndex); + if (!submesh) { + throw DeadlyImportError(Formatter::format() << "Ogre Mesh does not include submesh " << submeshIndex << " referenced in M_SUBMESH_NAME_TABLE_ELEMENT. Invalid mesh file."); + } + + submesh->name = ReadLine(); + DefaultLogger::get()->debug(Formatter::format() << " - SubMesh " << submesh->index << " name '" << submesh->name << "'"); + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } +} + +void OgreBinarySerializer::ReadGeometry(VertexData *dest) +{ + dest->count = Read(); + + DefaultLogger::get()->debug(Formatter::format() << " - Reading geometry of " << dest->count << " vertices"); + + if (!AtEnd()) + { + uint16_t id = ReadHeader(); + while (!AtEnd() && + (id == M_GEOMETRY_VERTEX_DECLARATION || + id == M_GEOMETRY_VERTEX_BUFFER)) + { + switch(id) + { + case M_GEOMETRY_VERTEX_DECLARATION: + { + ReadGeometryVertexDeclaration(dest); + break; + } + case M_GEOMETRY_VERTEX_BUFFER: + { + ReadGeometryVertexBuffer(dest); + break; + } + } + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } +} + +void OgreBinarySerializer::ReadGeometryVertexDeclaration(VertexData *dest) +{ + if (!AtEnd()) + { + uint16_t id = ReadHeader(); + while (!AtEnd() && id == M_GEOMETRY_VERTEX_ELEMENT) + { + ReadGeometryVertexElement(dest); + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } +} + +void OgreBinarySerializer::ReadGeometryVertexElement(VertexData *dest) +{ + VertexElement element; + element.source = Read(); + element.type = static_cast(Read()); + element.semantic = static_cast(Read()); + element.offset = Read(); + element.index = Read(); + + DefaultLogger::get()->debug(Formatter::format() << " - Vertex element " << element.SemanticToString() << " of type " + << element.TypeToString() << " index=" << element.index << " source=" << element.source); + + dest->vertexElements.push_back(element); +} + +void OgreBinarySerializer::ReadGeometryVertexBuffer(VertexData *dest) +{ + uint16_t bindIndex = Read(); + uint16_t vertexSize = Read(); + + uint16_t id = ReadHeader(); + if (id != M_GEOMETRY_VERTEX_BUFFER_DATA) + throw DeadlyImportError("M_GEOMETRY_VERTEX_BUFFER_DATA not found in M_GEOMETRY_VERTEX_BUFFER"); + + if (dest->VertexSize(bindIndex) != vertexSize) + throw DeadlyImportError("Vertex buffer size does not agree with vertex declaration in M_GEOMETRY_VERTEX_BUFFER"); + + size_t numBytes = dest->count * vertexSize; + uint8_t *vertexBuffer = ReadBytes(numBytes); + dest->vertexBindings[bindIndex] = MemoryStreamPtr(new Assimp::MemoryIOStream(vertexBuffer, numBytes, true)); + + DefaultLogger::get()->debug(Formatter::format() << " - Read vertex buffer for source " << bindIndex << " of " << numBytes << " bytes"); +} + +void OgreBinarySerializer::ReadEdgeList(Mesh *mesh) +{ + // Assimp does not acknowledge LOD levels as far as I can see it. This info is just skipped. + + if (!AtEnd()) + { + uint16_t id = ReadHeader(); + while (!AtEnd() && id == M_EDGE_LIST_LOD) + { + m_reader->IncPtr(sizeof(uint16_t)); // lod index + bool manual = Read(); + + if (!manual) + { + m_reader->IncPtr(sizeof(uint8_t)); + uint32_t numTriangles = Read(); + uint32_t numEdgeGroups = Read(); + + size_t skipBytes = (sizeof(uint32_t) * 8 + sizeof(float) * 4) * numTriangles; + m_reader->IncPtr(skipBytes); + + for (size_t i=0; iIncPtr(sizeof(uint32_t) * 3); + uint32_t numEdges = Read(); + for (size_t j=0; jIncPtr(sizeof(uint32_t) * 6 + sizeof(uint8_t)); + } + } + } + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } +} + +void OgreBinarySerializer::ReadPoses(Mesh *mesh) +{ + if (!AtEnd()) + { + uint16_t id = ReadHeader(); + while (!AtEnd() && id == M_POSE) + { + Pose *pose = new Pose(); + pose->name = ReadLine(); + pose->target = Read(); + pose->hasNormals = Read(); + + ReadPoseVertices(pose); + + mesh->poses.push_back(pose); + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } +} + +void OgreBinarySerializer::ReadPoseVertices(Pose *pose) +{ + if (!AtEnd()) + { + uint16_t id = ReadHeader(); + while (!AtEnd() && id == M_POSE_VERTEX) + { + Pose::Vertex v; + v.index = Read(); + ReadVector(v.offset); + if (pose->hasNormals) + ReadVector(v.normal); + + pose->vertices[v.index] = v; + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } +} + +void OgreBinarySerializer::ReadAnimations(Mesh *mesh) +{ + if (!AtEnd()) + { + uint16_t id = ReadHeader(); + while (!AtEnd() && id == M_ANIMATION) + { + Animation *anim = new Animation(mesh); + anim->name = ReadLine(); + anim->length = Read(); + + ReadAnimation(anim); + + mesh->animations.push_back(anim); + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } +} + +void OgreBinarySerializer::ReadAnimation(Animation *anim) +{ + if (!AtEnd()) + { + uint16_t id = ReadHeader(); + if (id == M_ANIMATION_BASEINFO) + { + anim->baseName = ReadLine(); + anim->baseTime = Read(); + + // Advance to first track + id = ReadHeader(); + } + + while (!AtEnd() && id == M_ANIMATION_TRACK) + { + VertexAnimationTrack track; + track.type = static_cast(Read()); + track.target = Read(); + + ReadAnimationKeyFrames(anim, &track); + + anim->tracks.push_back(track); + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } +} + +void OgreBinarySerializer::ReadAnimationKeyFrames(Animation *anim, VertexAnimationTrack *track) +{ + if (!AtEnd()) + { + uint16_t id = ReadHeader(); + while (!AtEnd() && + (id == M_ANIMATION_MORPH_KEYFRAME || + id == M_ANIMATION_POSE_KEYFRAME)) + { + if (id == M_ANIMATION_MORPH_KEYFRAME) + { + MorphKeyFrame kf; + kf.timePos = Read(); + bool hasNormals = Read(); + + size_t vertexCount = anim->AssociatedVertexData(track)->count; + size_t vertexSize = sizeof(float) * (hasNormals ? 6 : 3); + size_t numBytes = vertexCount * vertexSize; + + uint8_t *morphBuffer = ReadBytes(numBytes); + kf.buffer = MemoryStreamPtr(new Assimp::MemoryIOStream(morphBuffer, numBytes, true)); + + track->morphKeyFrames.push_back(kf); + } + else if (id == M_ANIMATION_POSE_KEYFRAME) + { + PoseKeyFrame kf; + kf.timePos = Read(); + + if (!AtEnd()) + { + id = ReadHeader(); + while (!AtEnd() && id == M_ANIMATION_POSE_REF) + { + PoseRef pr; + pr.index = Read(); + pr.influence = Read(); + kf.references.push_back(pr); + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } + + track->poseKeyFrames.push_back(kf); + } + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } +} + +// Skeleton + +bool OgreBinarySerializer::ImportSkeleton(Assimp::IOSystem *pIOHandler, Mesh *mesh) +{ + if (!mesh || mesh->skeletonRef.empty()) + return false; + + // Highly unusual to see in read world cases but support + // binary mesh referencing a XML skeleton file. + if (EndsWith(mesh->skeletonRef, ".skeleton.xml", false)) + { + OgreXmlSerializer::ImportSkeleton(pIOHandler, mesh); + return false; + } + + MemoryStreamReaderPtr reader = OpenReader(pIOHandler, mesh->skeletonRef); + + Skeleton *skeleton = new Skeleton(); + OgreBinarySerializer serializer(reader.get(), OgreBinarySerializer::AM_Skeleton); + serializer.ReadSkeleton(skeleton); + mesh->skeleton = skeleton; + return true; +} + +bool OgreBinarySerializer::ImportSkeleton(Assimp::IOSystem *pIOHandler, MeshXml *mesh) +{ + if (!mesh || mesh->skeletonRef.empty()) + return false; + + MemoryStreamReaderPtr reader = OpenReader(pIOHandler, mesh->skeletonRef); + if (!reader.get()) + return false; + + Skeleton *skeleton = new Skeleton(); + OgreBinarySerializer serializer(reader.get(), OgreBinarySerializer::AM_Skeleton); + serializer.ReadSkeleton(skeleton); + mesh->skeleton = skeleton; + return true; +} + +MemoryStreamReaderPtr OgreBinarySerializer::OpenReader(Assimp::IOSystem *pIOHandler, const std::string &filename) +{ + if (!EndsWith(filename, ".skeleton", false)) + { + DefaultLogger::get()->error("Imported Mesh is referencing to unsupported '" + filename + "' skeleton file."); + return MemoryStreamReaderPtr(); + } + + if (!pIOHandler->Exists(filename)) + { + DefaultLogger::get()->error("Failed to find skeleton file '" + filename + "' that is referenced by imported Mesh."); + return MemoryStreamReaderPtr(); + } + + IOStream *f = pIOHandler->Open(filename, "rb"); + if (!f) { + throw DeadlyImportError("Failed to open skeleton file " + filename); + } + + return MemoryStreamReaderPtr(new MemoryStreamReader(f)); +} + +void OgreBinarySerializer::ReadSkeleton(Skeleton *skeleton) +{ + uint16_t id = ReadHeader(false); + if (id != HEADER_CHUNK_ID) { + throw DeadlyExportError("Invalid Ogre Skeleton file header."); + } + + // This deserialization supports both versions of the skeleton spec + std::string version = ReadLine(); + if (version != SKELETON_VERSION_1_8 && version != SKELETON_VERSION_1_1) + { + throw DeadlyExportError(Formatter::format() << "Skeleton version " << version << " not supported by this importer." + << " Supported versions: " << SKELETON_VERSION_1_8 << " and " << SKELETON_VERSION_1_1); + } + + DefaultLogger::get()->debug("Reading Skeleton"); + + bool firstBone = true; + bool firstAnim = true; + + while (!AtEnd()) + { + id = ReadHeader(); + switch(id) + { + case SKELETON_BLENDMODE: + { + skeleton->blendMode = static_cast(Read()); + break; + } + case SKELETON_BONE: + { + if (firstBone) + { + DefaultLogger::get()->debug(" - Bones"); + firstBone = false; + } + + ReadBone(skeleton); + break; + } + case SKELETON_BONE_PARENT: + { + ReadBoneParent(skeleton); + break; + } + case SKELETON_ANIMATION: + { + if (firstAnim) + { + DefaultLogger::get()->debug(" - Animations"); + firstAnim = false; + } + + ReadSkeletonAnimation(skeleton); + break; + } + case SKELETON_ANIMATION_LINK: + { + ReadSkeletonAnimationLink(skeleton); + break; + } + } + } + + // Calculate bone matrices for root bones. Recursively calculates their children. + for (size_t i=0, len=skeleton->bones.size(); ibones[i]; + if (!bone->IsParented()) + bone->CalculateWorldMatrixAndDefaultPose(skeleton); + } +} + +void OgreBinarySerializer::ReadBone(Skeleton *skeleton) +{ + Bone *bone = new Bone(); + bone->name = ReadLine(); + bone->id = Read(); + + // Pos and rot + ReadVector(bone->position); + ReadQuaternion(bone->rotation); + + // Scale (optional) + if (m_currentLen > MSTREAM_BONE_SIZE_WITHOUT_SCALE) + ReadVector(bone->scale); + + // Bone indexes need to start from 0 and be contiguous + if (bone->id != skeleton->bones.size()) { + throw DeadlyImportError(Formatter::format() << "Ogre Skeleton bone indexes not contiguous. Error at bone index " << bone->id); + } + + DefaultLogger::get()->debug(Formatter::format() << " " << bone->id << " " << bone->name); + + skeleton->bones.push_back(bone); +} + +void OgreBinarySerializer::ReadBoneParent(Skeleton *skeleton) +{ + uint16_t childId = Read(); + uint16_t parentId = Read(); + + Bone *child = skeleton->BoneById(childId); + Bone *parent = skeleton->BoneById(parentId); + + if (child && parent) + parent->AddChild(child); + else + throw DeadlyImportError(Formatter::format() << "Failed to find bones for parenting: Child id " << childId << " for parent id " << parentId); +} + +void OgreBinarySerializer::ReadSkeletonAnimation(Skeleton *skeleton) +{ + Animation *anim = new Animation(skeleton); + anim->name = ReadLine(); + anim->length = Read(); + + if (!AtEnd()) + { + uint16_t id = ReadHeader(); + if (id == SKELETON_ANIMATION_BASEINFO) + { + anim->baseName = ReadLine(); + anim->baseTime = Read(); + + // Advance to first track + id = ReadHeader(); + } + + while (!AtEnd() && id == SKELETON_ANIMATION_TRACK) + { + ReadSkeletonAnimationTrack(skeleton, anim); + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + } + + skeleton->animations.push_back(anim); + + DefaultLogger::get()->debug(Formatter::format() << " " << anim->name << " (" << anim->length << " sec, " << anim->tracks.size() << " tracks)"); +} + +void OgreBinarySerializer::ReadSkeletonAnimationTrack(Skeleton *skeleton, Animation *dest) +{ + uint16_t boneId = Read(); + Bone *bone = dest->parentSkeleton->BoneById(boneId); + if (!bone) { + throw DeadlyImportError(Formatter::format() << "Cannot read animation track, target bone " << boneId << " not in target Skeleton"); + } + + VertexAnimationTrack track; + track.type = VertexAnimationTrack::VAT_TRANSFORM; + track.boneName = bone->name; + + uint16_t id = ReadHeader(); + while (!AtEnd() && id == SKELETON_ANIMATION_TRACK_KEYFRAME) + { + ReadSkeletonAnimationKeyFrame(&track); + + if (!AtEnd()) + id = ReadHeader(); + } + if (!AtEnd()) + RollbackHeader(); + + dest->tracks.push_back(track); +} + +void OgreBinarySerializer::ReadSkeletonAnimationKeyFrame(VertexAnimationTrack *dest) +{ + TransformKeyFrame keyframe; + keyframe.timePos = Read(); + + // Rot and pos + ReadQuaternion(keyframe.rotation); + ReadVector(keyframe.position); + + // Scale (optional) + if (m_currentLen > MSTREAM_KEYFRAME_SIZE_WITHOUT_SCALE) + ReadVector(keyframe.scale); + + dest->transformKeyFrames.push_back(keyframe); +} + +void OgreBinarySerializer::ReadSkeletonAnimationLink(Skeleton *skeleton) +{ + // Skip bounds, not compatible with Assimp. + ReadLine(); // skeleton name + SkipBytes(sizeof(float) * 3); // scale +} + +} // Ogre +} // Assimp + +#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER diff --git a/code/OgreBinarySerializer.h b/code/OgreBinarySerializer.h new file mode 100644 index 000000000..69db4b722 --- /dev/null +++ b/code/OgreBinarySerializer.h @@ -0,0 +1,416 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#ifndef AI_OGREBINARYSERIALIZER_H_INC +#define AI_OGREBINARYSERIALIZER_H_INC + +#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER + +#include "OgreStructs.h" + +namespace Assimp +{ +namespace Ogre +{ + +typedef Assimp::StreamReaderLE MemoryStreamReader; +typedef boost::shared_ptr MemoryStreamReaderPtr; + +class OgreBinarySerializer +{ +public: + /// Imports mesh and returns the result. + /** @note Fatal unrecoverable errors will throw a DeadlyImportError. */ + static Mesh *ImportMesh(MemoryStreamReader *reader); + + /// Imports skeleton to @c mesh into Mesh::skeleton. + /** If mesh does not have a skeleton reference or the skeleton file + cannot be found it is not a fatal DeadlyImportError. + @return If skeleton import was successful. */ + static bool ImportSkeleton(Assimp::IOSystem *pIOHandler, Mesh *mesh); + static bool ImportSkeleton(Assimp::IOSystem *pIOHandler, MeshXml *mesh); + +private: + enum AssetMode + { + AM_Mesh, + AM_Skeleton + }; + + OgreBinarySerializer(MemoryStreamReader *reader, AssetMode mode) : + m_reader(reader), + m_currentLen(0), + assetMode(mode) + { + } + + static MemoryStreamReaderPtr OpenReader(Assimp::IOSystem *pIOHandler, const std::string &filename); + + // Header + + uint16_t ReadHeader(bool readLen = true); + void RollbackHeader(); + + // Mesh + + void ReadMesh(Mesh *mesh); + void ReadMeshLodInfo(Mesh *mesh); + void ReadMeshSkeletonLink(Mesh *mesh); + void ReadMeshBounds(Mesh *mesh); + void ReadMeshExtremes(Mesh *mesh); + + void ReadSubMesh(Mesh *mesh); + void ReadSubMeshNames(Mesh *mesh); + void ReadSubMeshOperation(SubMesh *submesh); + void ReadSubMeshTextureAlias(SubMesh *submesh); + + void ReadBoneAssignment(VertexData *dest); + + void ReadGeometry(VertexData *dest); + void ReadGeometryVertexDeclaration(VertexData *dest); + void ReadGeometryVertexElement(VertexData *dest); + void ReadGeometryVertexBuffer(VertexData *dest); + + void ReadEdgeList(Mesh *mesh); + void ReadPoses(Mesh *mesh); + void ReadPoseVertices(Pose *pose); + + void ReadAnimations(Mesh *mesh); + void ReadAnimation(Animation *anim); + void ReadAnimationKeyFrames(Animation *anim, VertexAnimationTrack *track); + + void NormalizeBoneWeights(VertexData *vertexData) const; + + // Skeleton + + void ReadSkeleton(Skeleton *skeleton); + + void ReadBone(Skeleton *skeleton); + void ReadBoneParent(Skeleton *skeleton); + + void ReadSkeletonAnimation(Skeleton *skeleton); + void ReadSkeletonAnimationTrack(Skeleton *skeleton, Animation *dest); + void ReadSkeletonAnimationKeyFrame(VertexAnimationTrack *dest); + void ReadSkeletonAnimationLink(Skeleton *skeleton); + + // Reader utils + bool AtEnd() const; + + template + inline T Read(); + + void ReadBytes(char *dest, size_t numBytes); + void ReadBytes(uint8_t *dest, size_t numBytes); + void ReadBytes(void *dest, size_t numBytes); + uint8_t *ReadBytes(size_t numBytes); + + void ReadVector(aiVector3D &vec); + void ReadQuaternion(aiQuaternion &quat); + + std::string ReadString(size_t len); + std::string ReadLine(); + + void SkipBytes(size_t numBytes); + + uint32_t m_currentLen; + MemoryStreamReader *m_reader; + + AssetMode assetMode; +}; + +enum MeshChunkId +{ + M_HEADER = 0x1000, + // char* version : Version number check + M_MESH = 0x3000, + // bool skeletallyAnimated // important flag which affects h/w buffer policies + // Optional M_GEOMETRY chunk + M_SUBMESH = 0x4000, + // char* materialName + // bool useSharedVertices + // unsigned int indexCount + // bool indexes32Bit + // unsigned int* faceVertexIndices (indexCount) + // OR + // unsigned short* faceVertexIndices (indexCount) + // M_GEOMETRY chunk (Optional: present only if useSharedVertices = false) + M_SUBMESH_OPERATION = 0x4010, // optional, trilist assumed if missing + // unsigned short operationType + M_SUBMESH_BONE_ASSIGNMENT = 0x4100, + // Optional bone weights (repeating section) + // unsigned int vertexIndex; + // unsigned short boneIndex; + // float weight; + // Optional chunk that matches a texture name to an alias + // a texture alias is sent to the submesh material to use this texture name + // instead of the one in the texture unit with a matching alias name + M_SUBMESH_TEXTURE_ALIAS = 0x4200, // Repeating section + // char* aliasName; + // char* textureName; + + M_GEOMETRY = 0x5000, // NB this chunk is embedded within M_MESH and M_SUBMESH + // unsigned int vertexCount + M_GEOMETRY_VERTEX_DECLARATION = 0x5100, + M_GEOMETRY_VERTEX_ELEMENT = 0x5110, // Repeating section + // unsigned short source; // buffer bind source + // unsigned short type; // VertexElementType + // unsigned short semantic; // VertexElementSemantic + // unsigned short offset; // start offset in buffer in bytes + // unsigned short index; // index of the semantic (for colours and texture coords) + M_GEOMETRY_VERTEX_BUFFER = 0x5200, // Repeating section + // unsigned short bindIndex; // Index to bind this buffer to + // unsigned short vertexSize; // Per-vertex size, must agree with declaration at this index + M_GEOMETRY_VERTEX_BUFFER_DATA = 0x5210, + // raw buffer data + M_MESH_SKELETON_LINK = 0x6000, + // Optional link to skeleton + // char* skeletonName : name of .skeleton to use + M_MESH_BONE_ASSIGNMENT = 0x7000, + // Optional bone weights (repeating section) + // unsigned int vertexIndex; + // unsigned short boneIndex; + // float weight; + M_MESH_LOD = 0x8000, + // Optional LOD information + // string strategyName; + // unsigned short numLevels; + // bool manual; (true for manual alternate meshes, false for generated) + M_MESH_LOD_USAGE = 0x8100, + // Repeating section, ordered in increasing depth + // NB LOD 0 (full detail from 0 depth) is omitted + // LOD value - this is a distance, a pixel count etc, based on strategy + // float lodValue; + M_MESH_LOD_MANUAL = 0x8110, + // Required if M_MESH_LOD section manual = true + // String manualMeshName; + M_MESH_LOD_GENERATED = 0x8120, + // Required if M_MESH_LOD section manual = false + // Repeating section (1 per submesh) + // unsigned int indexCount; + // bool indexes32Bit + // unsigned short* faceIndexes; (indexCount) + // OR + // unsigned int* faceIndexes; (indexCount) + M_MESH_BOUNDS = 0x9000, + // float minx, miny, minz + // float maxx, maxy, maxz + // float radius + + // Added By DrEvil + // optional chunk that contains a table of submesh indexes and the names of + // the sub-meshes. + M_SUBMESH_NAME_TABLE = 0xA000, + // Subchunks of the name table. Each chunk contains an index & string + M_SUBMESH_NAME_TABLE_ELEMENT = 0xA100, + // short index + // char* name + // Optional chunk which stores precomputed edge data + M_EDGE_LISTS = 0xB000, + // Each LOD has a separate edge list + M_EDGE_LIST_LOD = 0xB100, + // unsigned short lodIndex + // bool isManual // If manual, no edge data here, loaded from manual mesh + // bool isClosed + // unsigned long numTriangles + // unsigned long numEdgeGroups + // Triangle* triangleList + // unsigned long indexSet + // unsigned long vertexSet + // unsigned long vertIndex[3] + // unsigned long sharedVertIndex[3] + // float normal[4] + + M_EDGE_GROUP = 0xB110, + // unsigned long vertexSet + // unsigned long triStart + // unsigned long triCount + // unsigned long numEdges + // Edge* edgeList + // unsigned long triIndex[2] + // unsigned long vertIndex[2] + // unsigned long sharedVertIndex[2] + // bool degenerate + // Optional poses section, referred to by pose keyframes + M_POSES = 0xC000, + M_POSE = 0xC100, + // char* name (may be blank) + // unsigned short target // 0 for shared geometry, + // 1+ for submesh index + 1 + // bool includesNormals [1.8+] + M_POSE_VERTEX = 0xC111, + // unsigned long vertexIndex + // float xoffset, yoffset, zoffset + // float xnormal, ynormal, znormal (optional, 1.8+) + // Optional vertex animation chunk + M_ANIMATIONS = 0xD000, + M_ANIMATION = 0xD100, + // char* name + // float length + M_ANIMATION_BASEINFO = 0xD105, + // [Optional] base keyframe information (pose animation only) + // char* baseAnimationName (blank for self) + // float baseKeyFrameTime + M_ANIMATION_TRACK = 0xD110, + // unsigned short type // 1 == morph, 2 == pose + // unsigned short target // 0 for shared geometry, + // 1+ for submesh index + 1 + M_ANIMATION_MORPH_KEYFRAME = 0xD111, + // float time + // bool includesNormals [1.8+] + // float x,y,z // repeat by number of vertices in original geometry + M_ANIMATION_POSE_KEYFRAME = 0xD112, + // float time + M_ANIMATION_POSE_REF = 0xD113, // repeat for number of referenced poses + // unsigned short poseIndex + // float influence + // Optional submesh extreme vertex list chink + M_TABLE_EXTREMES = 0xE000, + // unsigned short submesh_index; + // float extremes [n_extremes][3]; +}; + +static std::string MeshHeaderToString(MeshChunkId id) +{ + switch(id) + { + case M_HEADER: return "HEADER"; + case M_MESH: return "MESH"; + case M_SUBMESH: return "SUBMESH"; + case M_SUBMESH_OPERATION: return "SUBMESH_OPERATION"; + case M_SUBMESH_BONE_ASSIGNMENT: return "SUBMESH_BONE_ASSIGNMENT"; + case M_SUBMESH_TEXTURE_ALIAS: return "SUBMESH_TEXTURE_ALIAS"; + case M_GEOMETRY: return "GEOMETRY"; + case M_GEOMETRY_VERTEX_DECLARATION: return "GEOMETRY_VERTEX_DECLARATION"; + case M_GEOMETRY_VERTEX_ELEMENT: return "GEOMETRY_VERTEX_ELEMENT"; + case M_GEOMETRY_VERTEX_BUFFER: return "GEOMETRY_VERTEX_BUFFER"; + case M_GEOMETRY_VERTEX_BUFFER_DATA: return "GEOMETRY_VERTEX_BUFFER_DATA"; + case M_MESH_SKELETON_LINK: return "MESH_SKELETON_LINK"; + case M_MESH_BONE_ASSIGNMENT: return "MESH_BONE_ASSIGNMENT"; + case M_MESH_LOD: return "MESH_LOD"; + case M_MESH_LOD_USAGE: return "MESH_LOD_USAGE"; + case M_MESH_LOD_MANUAL: return "MESH_LOD_MANUAL"; + case M_MESH_LOD_GENERATED: return "MESH_LOD_GENERATED"; + case M_MESH_BOUNDS: return "MESH_BOUNDS"; + case M_SUBMESH_NAME_TABLE: return "SUBMESH_NAME_TABLE"; + case M_SUBMESH_NAME_TABLE_ELEMENT: return "SUBMESH_NAME_TABLE_ELEMENT"; + case M_EDGE_LISTS: return "EDGE_LISTS"; + case M_EDGE_LIST_LOD: return "EDGE_LIST_LOD"; + case M_EDGE_GROUP: return "EDGE_GROUP"; + case M_POSES: return "POSES"; + case M_POSE: return "POSE"; + case M_POSE_VERTEX: return "POSE_VERTEX"; + case M_ANIMATIONS: return "ANIMATIONS"; + case M_ANIMATION: return "ANIMATION"; + case M_ANIMATION_BASEINFO: return "ANIMATION_BASEINFO"; + case M_ANIMATION_TRACK: return "ANIMATION_TRACK"; + case M_ANIMATION_MORPH_KEYFRAME: return "ANIMATION_MORPH_KEYFRAME"; + case M_ANIMATION_POSE_KEYFRAME: return "ANIMATION_POSE_KEYFRAME"; + case M_ANIMATION_POSE_REF: return "ANIMATION_POSE_REF"; + case M_TABLE_EXTREMES: return "TABLE_EXTREMES"; + } + return "Unknown_MeshChunkId"; +} + +enum SkeletonChunkId +{ + SKELETON_HEADER = 0x1000, + // char* version : Version number check + SKELETON_BLENDMODE = 0x1010, // optional + // unsigned short blendmode : SkeletonAnimationBlendMode + SKELETON_BONE = 0x2000, + // Repeating section defining each bone in the system. + // Bones are assigned indexes automatically based on their order of declaration + // starting with 0. + // char* name : name of the bone + // unsigned short handle : handle of the bone, should be contiguous & start at 0 + // Vector3 position : position of this bone relative to parent + // Quaternion orientation : orientation of this bone relative to parent + // Vector3 scale : scale of this bone relative to parent + SKELETON_BONE_PARENT = 0x3000, + // Record of the parent of a single bone, used to build the node tree + // Repeating section, listed in Bone Index order, one per Bone + // unsigned short handle : child bone + // unsigned short parentHandle : parent bone + SKELETON_ANIMATION = 0x4000, + // A single animation for this skeleton + // char* name : Name of the animation + // float length : Length of the animation in seconds + SKELETON_ANIMATION_BASEINFO = 0x4010, + // [Optional] base keyframe information + // char* baseAnimationName (blank for self) + // float baseKeyFrameTime + SKELETON_ANIMATION_TRACK = 0x4100, + // A single animation track (relates to a single bone) + // Repeating section (within SKELETON_ANIMATION) + // unsigned short boneIndex : Index of bone to apply to + SKELETON_ANIMATION_TRACK_KEYFRAME = 0x4110, + // A single keyframe within the track + // Repeating section + // float time : The time position (seconds) + // Quaternion rotate : Rotation to apply at this keyframe + // Vector3 translate : Translation to apply at this keyframe + // Vector3 scale : Scale to apply at this keyframe + SKELETON_ANIMATION_LINK = 0x5000 + // Link to another skeleton, to re-use its animations + // char* skeletonName : name of skeleton to get animations from + // float scale : scale to apply to trans/scale keys +}; + +static std::string SkeletonHeaderToString(SkeletonChunkId id) +{ + switch(id) + { + case SKELETON_HEADER: return "HEADER"; + case SKELETON_BLENDMODE: return "BLENDMODE"; + case SKELETON_BONE: return "BONE"; + case SKELETON_BONE_PARENT: return "BONE_PARENT"; + case SKELETON_ANIMATION: return "ANIMATION"; + case SKELETON_ANIMATION_BASEINFO: return "ANIMATION_BASEINFO"; + case SKELETON_ANIMATION_TRACK: return "ANIMATION_TRACK"; + case SKELETON_ANIMATION_TRACK_KEYFRAME: return "ANIMATION_TRACK_KEYFRAME"; + case SKELETON_ANIMATION_LINK: return "ANIMATION_LINK"; + } + return "Unknown_SkeletonChunkId"; +} +} // Ogre +} // Assimp + +#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER +#endif // AI_OGREBINARYSERIALIZER_H_INC diff --git a/code/OgreImporter.cpp b/code/OgreImporter.cpp index dbf6e8105..fbb50cfc3 100644 --- a/code/OgreImporter.cpp +++ b/code/OgreImporter.cpp @@ -38,32 +38,27 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ -#include "AssimpPCH.h" - #ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER -#include -#include +#include "AssimpPCH.h" #include "OgreImporter.h" -#include "TinyFormatter.h" -#include "irrXMLWrapper.h" +#include "OgreBinarySerializer.h" +#include "OgreXmlSerializer.h" static const aiImporterDesc desc = { - "Ogre XML Mesh Importer", + "Ogre3D Mesh Importer", "", "", "", - aiImporterFlags_SupportTextFlavour, + aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour, 0, 0, 0, 0, - "mesh.xml" + "mesh mesh.xml" }; -using namespace std; - namespace Assimp { namespace Ogre @@ -83,174 +78,67 @@ void OgreImporter::SetupProperties(const Importer* pImp) bool OgreImporter::CanRead(const std::string &pFile, Assimp::IOSystem *pIOHandler, bool checkSig) const { if (!checkSig) { - return EndsWith(pFile, ".mesh.xml", false); + return EndsWith(pFile, ".mesh.xml", false) || EndsWith(pFile, ".mesh", false); } - const char* tokens[] = { "" }; - return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); + if (EndsWith(pFile, ".mesh.xml", false)) + { + const char* tokens[] = { "" }; + return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); + } + else + { + /// @todo Read and validate first header chunk? + return EndsWith(pFile, ".mesh", false); + } } void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Assimp::IOSystem *pIOHandler) { - // -------------------- Initial file and XML operations -------------------- - - // Open - boost::scoped_ptr file(pIOHandler->Open(pFile)); - if (!file.get()) { + // Open source file + IOStream *f = pIOHandler->Open(pFile, "rb"); + if (!f) { throw DeadlyImportError("Failed to open file " + pFile); } - // Read - boost::scoped_ptr xmlStream(new CIrrXML_IOStreamReader(file.get())); - boost::scoped_ptr reader(irr::io::createIrrXMLReader(xmlStream.get())); - if (!reader) { - throw DeadlyImportError("Failed to create XML Reader for " + pFile); - } - - DefaultLogger::get()->debug("Opened a XML reader for " + pFile); - - // Read root node - NextNode(reader.get()); - if (!CurrentNodeNameEquals(reader.get(), "mesh")) { - throw DeadlyImportError("Root node is not but <" + string(reader->getNodeName()) + "> in " + pFile); - } - - // Node names - const string nnSharedGeometry = "sharedgeometry"; - const string nnVertexBuffer = "vertexbuffer"; - const string nnSubMeshes = "submeshes"; - const string nnSubMesh = "submesh"; - const string nnSubMeshNames = "submeshnames"; - const string nnSkeletonLink = "skeletonlink"; - - // -------------------- Shared Geometry -------------------- - // This can be used to share geometry between submeshes - - NextNode(reader.get()); - if (CurrentNodeNameEquals(reader.get(), nnSharedGeometry)) + // Binary .mesh import + if (EndsWith(pFile, ".mesh", false)) { - DefaultLogger::get()->debug("Reading shared geometry"); - unsigned int NumVertices = GetAttribute(reader.get(), "vertexcount"); + /// @note MemoryStreamReader takes ownership of f. + MemoryStreamReader reader(f); - NextNode(reader.get()); - while(CurrentNodeNameEquals(reader.get(), nnVertexBuffer)) { - ReadVertexBuffer(m_SharedGeometry, reader.get(), NumVertices); - } - } - - // -------------------- Sub Meshes -------------------- - - if (!CurrentNodeNameEquals(reader.get(), nnSubMeshes)) { - throw DeadlyImportError("Could not find node inside root node"); - } - - vector > subMeshes; - vector materials; - - NextNode(reader.get()); - while(CurrentNodeNameEquals(reader.get(), nnSubMesh)) - { - SubMesh* submesh = new SubMesh(); - ReadSubMesh(subMeshes.size(), *submesh, reader.get()); - - // Just a index in a array, we add a mesh in each loop cycle, so we get indicies like 0, 1, 2 ... n; - // so it is important to do this before pushing the mesh in the vector! - /// @todo Not sure if this really is needed, refactor out if possible. - submesh->MaterialIndex = subMeshes.size(); - - subMeshes.push_back(boost::shared_ptr(submesh)); - - /** @todo What is the correct way of handling empty ref here. - Does Assimp require there to be a valid material index for each mesh, - even if its a dummy material. */ - aiMaterial* material = ReadMaterial(pFile, pIOHandler, submesh->MaterialName); - materials.push_back(material); - } - - if (subMeshes.empty()) { - throw DeadlyImportError("Could not find node inside root node"); - } - - // This is really a internal error if we failed to create dummy materials. - if (subMeshes.size() != materials.size()) { - throw DeadlyImportError("Internal Error: Material count does not match the submesh count"); - } - - // Skip submesh names. - /// @todo Should these be read to scene etc. metadata? - if (CurrentNodeNameEquals(reader.get(), nnSubMeshNames)) - { - NextNode(reader.get()); - while(CurrentNodeNameEquals(reader.get(), nnSubMesh)) { - NextNode(reader.get()); - } - } - - // -------------------- Skeleton -------------------- - - vector Bones; - vector Animations; - - if (CurrentNodeNameEquals(reader.get(), nnSkeletonLink)) - { - string skeletonFile = GetAttribute(reader.get(), "name"); - if (!skeletonFile.empty()) - { - ReadSkeleton(pFile, pIOHandler, pScene, skeletonFile, Bones, Animations); - } - else - { - DefaultLogger::get()->debug("Found a unusual <" + nnSkeletonLink + "> with a empty file reference"); - } - NextNode(reader.get()); + // Import mesh + boost::scoped_ptr mesh(OgreBinarySerializer::ImportMesh(&reader)); + + // Import skeleton + OgreBinarySerializer::ImportSkeleton(pIOHandler, mesh.get()); + + // Import mesh referenced materials + ReadMaterials(pFile, pIOHandler, pScene, mesh.get()); + + // Convert to Assimp + mesh->ConvertToAssimpScene(pScene); } + // XML .mesh.xml import else { - DefaultLogger::get()->debug("Mesh has no assigned skeleton with <" + nnSkeletonLink + ">"); + /// @note XmlReader does not take ownership of f, hence the scoped ptr. + boost::scoped_ptr scopedFile(f); + boost::scoped_ptr xmlStream(new CIrrXML_IOStreamReader(scopedFile.get())); + boost::scoped_ptr reader(irr::io::createIrrXMLReader(xmlStream.get())); + + // Import mesh + boost::scoped_ptr mesh(OgreXmlSerializer::ImportMesh(reader.get())); + + // Import skeleton + OgreXmlSerializer::ImportSkeleton(pIOHandler, mesh.get()); + + // Import mesh referenced materials + ReadMaterials(pFile, pIOHandler, pScene, mesh.get()); + + // Convert to Assimp + mesh->ConvertToAssimpScene(pScene); } - - // Now there might be for the shared geometry - if (CurrentNodeNameEquals(reader.get(), "boneassignments")) { - ReadBoneWeights(m_SharedGeometry, reader.get()); - } - - // -------------------- Process Results -------------------- - BOOST_FOREACH(boost::shared_ptr submesh, subMeshes) - { - ProcessSubMesh(*submesh.get(), m_SharedGeometry); - } - - // -------------------- Apply to aiScene -------------------- - - // Materials - pScene->mMaterials = new aiMaterial*[materials.size()]; - pScene->mNumMaterials = materials.size(); - - for(size_t i=0, len=materials.size(); imMaterials[i] = materials[i]; - } - - // Meshes - pScene->mMeshes = new aiMesh*[subMeshes.size()]; - pScene->mNumMeshes = subMeshes.size(); - - for(size_t i=0, len=subMeshes.size(); i submesh = subMeshes[i]; - pScene->mMeshes[i] = CreateAssimpSubMesh(pScene, *(submesh.get()), Bones); - } - - // Create the root node - pScene->mRootNode = new aiNode(); - pScene->mRootNode->mMeshes = new unsigned int[subMeshes.size()]; - pScene->mRootNode->mNumMeshes = subMeshes.size(); - - for(size_t i=0, len=subMeshes.size(); imRootNode->mMeshes[i] = static_cast(i); - } - - // Skeleton and animations - CreateAssimpSkeleton(pScene, Bones, Animations); } } // Ogre diff --git a/code/OgreImporter.h b/code/OgreImporter.h index e40809073..892696407 100644 --- a/code/OgreImporter.h +++ b/code/OgreImporter.h @@ -1,3 +1,42 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ #ifndef AI_OGREIMPORTER_H_INC #define AI_OGREIMPORTER_H_INC @@ -5,6 +44,8 @@ #ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER #include "BaseImporter.h" + +#include "OgreStructs.h" #include "OgreParsingUtils.h" namespace Assimp @@ -12,58 +53,10 @@ namespace Assimp namespace Ogre { -struct Face; -struct BoneWeight; -struct Bone; -struct Animation; - -/// Ogre SubMesh -struct SubMesh -{ - bool UseSharedGeometry; - bool Use32bitIndexes; - - std::string Name; - std::string MaterialName; - - bool HasGeometry; - bool HasPositions; - bool HasNormals; - bool HasTangents; - - std::vector Faces; - std::vector Positions; - std::vector Normals; - std::vector Tangents; - - /// Arbitrary number of texcoords, they are nearly always 2d, but Assimp has always 3d texcoords, n vectors(outer) with texcoords for each vertex(inner). - std::vector > Uvs; - - /// A list(inner) of bones for each vertex(outer). - std::vector > Weights; - - /// The Index in the Assimp material array from the material witch is attached to this submesh. - int MaterialIndex; - - // The highest index of a bone from a bone weight, this is needed to create the Assimp bone struct. Converting from vertex-bones to bone-vertices. - unsigned int BonesUsed; - - SubMesh() : - UseSharedGeometry(false), - Use32bitIndexes(false), - HasGeometry(false), - HasPositions(false), - HasNormals(false), - HasTangents(false), - MaterialIndex(-1), - BonesUsed(0) - { - } -}; - /** Importer for Ogre mesh, skeleton and material formats. - @todo Support vertex colors - @todo Support multiple TexCoords (this is already done??) */ + @todo Support vertex colors. + @todo Support poses/animations from the mesh file. + Currently only skeleton file animations are supported. */ class OgreImporter : public BaseImporter { public: @@ -80,40 +73,11 @@ public: virtual void SetupProperties(const Importer *pImp); private: - //-------------------------------- OgreMesh.cpp ------------------------------- - - /// Helper Functions to read parts of the XML File. - void ReadSubMesh(const unsigned int submeshIndex, SubMesh &submesh, XmlReader *reader); - - /// Reads a single Vertexbuffer and writes its data in the Submesh. - static void ReadVertexBuffer(SubMesh &submesh, XmlReader *reader, const unsigned int numVertices); - - /// Reads bone weights are stores them into the given submesh. - static void ReadBoneWeights(SubMesh &submesh, XmlReader *reader); - - /// After Loading a SubMehs some work needs to be done (make all Vertexes unique, normalize weights). - static void ProcessSubMesh(SubMesh &submesh, SubMesh &sharedGeometry); - - /// Uses the bone data to convert a SubMesh into a aiMesh which will be created and returned. - aiMesh *CreateAssimpSubMesh(aiScene *pScene, const SubMesh &submesh, const std::vector &bones) const; - - //-------------------------------- OgreSkeleton.cpp ------------------------------- - - /// Writes the results in Bones and Animations, Filename is not const, because its call-by-value and the function will change it! - void ReadSkeleton(const std::string &pFile, Assimp::IOSystem *pIOHandler, const aiScene *pScene, - const std::string &skeletonFile, std::vector &Bones, std::vector &Animations) const; - - /// Converts the animations in aiAnimations and puts them into the scene. - void PutAnimationsInScene(aiScene *pScene, const std::vector &Bones, const std::vector &Animations); - - /// Creates the aiSkeleton in current scene. - void CreateAssimpSkeleton(aiScene *pScene, const std::vector &bones, const std::vector &animations); - - /// Recursively creates a filled aiNode from a given root bone. - static aiNode* CreateNodeFromBone(int boneId, const std::vector &bones, aiNode *parent); - - //-------------------------------- OgreMaterial.cpp ------------------------------- - + /// Read materials referenced by the @c mesh to @c pScene. + void ReadMaterials(const std::string &pFile, Assimp::IOSystem *pIOHandler, aiScene *pScene, Mesh *mesh); + void ReadMaterials(const std::string &pFile, Assimp::IOSystem *pIOHandler, aiScene *pScene, MeshXml *mesh); + void AssignMaterials(aiScene *pScene, std::vector &materials); + /// Reads material aiMaterial* ReadMaterial(const std::string &pFile, Assimp::IOSystem *pIOHandler, const std::string MaterialName); @@ -125,104 +89,8 @@ private: std::string m_userDefinedMaterialLibFile; bool m_detectTextureTypeFromFilename; - /// VertexBuffer for the sub meshes that use shader geometry. - SubMesh m_SharedGeometry; - std::map m_textures; }; - -/// Simplified face. -/** @todo Support other polygon types than just just triangles. Move to using aiFace. */ -struct Face -{ - unsigned int VertexIndices[3]; -}; - -/// Ogre Bone assignment -struct BoneAssignment -{ - /// Bone ID from Ogre. - unsigned int BoneId; - // Bone name for Assimp. - std::string BoneName; -}; - -/// Ogre Bone weight -struct BoneWeight -{ - /// Bone Id - unsigned int Id; - /// BoneWeight - float Value; -}; - - -/// Ogre Bone -struct Bone -{ - std::string Name; - - int Id; - int ParentId; - - aiVector3D Position; - aiVector3D RotationAxis; - float RotationAngle; - - aiMatrix4x4 BoneToWorldSpace; - - std::vector Children; - - Bone() : - Id(-1), - ParentId(-1), - RotationAngle(0.0f) - { - } - - /// Returns if this bone is parented. - bool IsParented() const { return (ParentId != -1); } - - /// This operator is needed to sort the bones by Id in a vector. - bool operator<(const Bone &other) const { return (Id < other.Id); } - - /// This operator is needed to find a bone by its name in a vector - bool operator==(const std::string& other) const { return Name == other; } - bool operator==(const aiString& other) const { return Name == std::string(other.data); } - - /// @note Implemented in OgreSkeleton.cpp - void CalculateBoneToWorldSpaceMatrix(std::vector& Bones); -}; - -/// Ogre animation key frame -/** Transformations for a frame. */ -struct KeyFrame -{ - float Time; - aiVector3D Position; - aiQuaternion Rotation; - aiVector3D Scaling; -}; - -/// Ogre animation track -/** Keyframes for one bone. */ -struct Track -{ - std::string BoneName; - std::vector Keyframes; -}; - -/// Ogre animation -struct Animation -{ - /// Name - std::string Name; - /// Length - float Length; - /// Tracks - std::vector Tracks; -}; - } // Ogre } // Assimp diff --git a/code/OgreMaterial.cpp b/code/OgreMaterial.cpp index b284dbb31..4ef4b44ec 100644 --- a/code/OgreMaterial.cpp +++ b/code/OgreMaterial.cpp @@ -42,12 +42,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER -#include -#include - #include "OgreImporter.h" #include "TinyFormatter.h" +#include "fast_atof.h" + +#include +#include + using namespace std; namespace Assimp @@ -59,11 +61,66 @@ static const string partComment = "//"; static const string partBlockStart = "{"; static const string partBlockEnd = "}"; +void OgreImporter::ReadMaterials(const std::string &pFile, Assimp::IOSystem *pIOHandler, aiScene *pScene, Mesh *mesh) +{ + std::vector materials; + + // Create materials that can be found and parsed via the IOSystem. + for (size_t i=0, len=mesh->NumSubMeshes(); iGetSubMesh(i); + if (submesh && !submesh->materialRef.empty()) + { + aiMaterial *material = ReadMaterial(pFile, pIOHandler, submesh->materialRef); + if (material) + { + submesh->materialIndex = materials.size(); + materials.push_back(material); + } + } + } + + AssignMaterials(pScene, materials); +} + +void OgreImporter::ReadMaterials(const std::string &pFile, Assimp::IOSystem *pIOHandler, aiScene *pScene, MeshXml *mesh) +{ + std::vector materials; + + // Create materials that can be found and parsed via the IOSystem. + for (size_t i=0, len=mesh->NumSubMeshes(); iGetSubMesh(i); + if (submesh && !submesh->materialRef.empty()) + { + aiMaterial *material = ReadMaterial(pFile, pIOHandler, submesh->materialRef); + if (material) + { + submesh->materialIndex = materials.size(); + materials.push_back(material); + } + } + } + + AssignMaterials(pScene, materials); +} + +void OgreImporter::AssignMaterials(aiScene *pScene, std::vector &materials) +{ + pScene->mNumMaterials = materials.size(); + if (pScene->mNumMaterials > 0) + { + pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]; + for(size_t i=0;imNumMaterials; ++i) { + pScene->mMaterials[i] = materials[i]; + } + } +} + aiMaterial* OgreImporter::ReadMaterial(const std::string &pFile, Assimp::IOSystem *pIOHandler, const std::string materialName) { - /// @todo Should we return null ptr here or a empty material? if (materialName.empty()) { - return new aiMaterial(); + return 0; } // Full reference and examples of Ogre Material Script @@ -117,17 +174,15 @@ aiMaterial* OgreImporter::ReadMaterial(const std::string &pFile, Assimp::IOSyste } if (!materialFile) { - /// @todo Should we return null ptr here or a empty material? DefaultLogger::get()->error(Formatter::format() << "Failed to find source file for material '" << materialName << "'"); - return new aiMaterial(); + return 0; } boost::scoped_ptr stream(materialFile); if (stream->FileSize() == 0) { - /// @todo Should we return null ptr here or a empty material? DefaultLogger::get()->warn(Formatter::format() << "Source file for material '" << materialName << "' is empty (size is 0 bytes)"); - return new aiMaterial(); + return 0; } // Read bytes @@ -162,8 +217,7 @@ aiMaterial* OgreImporter::ReadMaterial(const std::string &pFile, Assimp::IOSyste // Skip commented lines if (linePart == partComment) { - string postComment = NextAfterNewLine(ss, linePart); - DefaultLogger::get()->debug("//" + postComment + " (comment line ignored)"); + NextAfterNewLine(ss, linePart); continue; } if (linePart != partMaterial) @@ -306,8 +360,7 @@ bool OgreImporter::ReadTechnique(const std::string &techniqueName, stringstream // Skip commented lines if (linePart == partComment) { - string postComment = SkipLine(ss); - DefaultLogger::get()->debug(" //" + postComment + " (comment line ignored)"); + SkipLine(ss); continue; } @@ -347,8 +400,7 @@ bool OgreImporter::ReadPass(const std::string &passName, stringstream &ss, aiMat // Skip commented lines if (linePart == partComment) { - string postComment = SkipLine(ss); - DefaultLogger::get()->debug(" //" + postComment + " (comment line ignored)"); + SkipLine(ss); continue; } @@ -416,8 +468,7 @@ bool OgreImporter::ReadTextureUnit(const std::string &textureUnitName, stringstr // Skip commented lines if (linePart == partComment) { - string postComment = SkipLine(ss); - DefaultLogger::get()->debug(" //" + postComment + " (comment line ignored)"); + SkipLine(ss); continue; } diff --git a/code/OgreMesh.cpp b/code/OgreMesh.cpp deleted file mode 100644 index ff481b1b4..000000000 --- a/code/OgreMesh.cpp +++ /dev/null @@ -1,569 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2012, assimp team -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -#include "AssimpPCH.h" - -#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER - -#include "OgreImporter.h" -#include "TinyFormatter.h" - -using namespace std; - -namespace Assimp -{ -namespace Ogre -{ - -void OgreImporter::ReadSubMesh(const unsigned int submeshIndex, SubMesh &submesh, XmlReader *reader) -{ - if (reader->getAttributeValue("material")) { - submesh.MaterialName = GetAttribute(reader, "material"); - } - if (reader->getAttributeValue("use32bitindexes")) { - submesh.Use32bitIndexes = GetAttribute(reader, "use32bitindexes"); - } - if (reader->getAttributeValue("usesharedvertices")) { - submesh.UseSharedGeometry = GetAttribute(reader, "usesharedvertices"); - } - - DefaultLogger::get()->debug(Formatter::format() << "Reading submesh " << submeshIndex); - DefaultLogger::get()->debug(Formatter::format() << " - Material '" << submesh.MaterialName << "'"); - DefaultLogger::get()->debug(Formatter::format() << " - Shader geometry = " << (submesh.UseSharedGeometry ? "true" : "false") << - ", 32bit indexes = " << (submesh.Use32bitIndexes ? "true" : "false")); - - //TODO: maybe we have alsways just 1 faces and 1 geometry and always in this order. this loop will only work correct, when the order - //of faces and geometry changed, and not if we have more than one of one - /// @todo Fix above comment with better read logic below - - NextNode(reader); - string currentNodeName = reader->getNodeName(); - - const string nnFaces = "faces"; - const string nnFace = "face"; - const string nnGeometry = "geometry"; - const string nnBoneAssignments = "boneassignments"; - const string nnVertexBuffer = "vertexbuffer"; - - bool quadWarned = false; - - while(currentNodeName == nnFaces || - currentNodeName == nnGeometry || - currentNodeName == nnBoneAssignments) - { - if (currentNodeName == nnFaces) - { - unsigned int numFaces = GetAttribute(reader, "count"); - - NextNode(reader); - currentNodeName = reader->getNodeName(); - - while(currentNodeName == nnFace) - { - Face NewFace; - NewFace.VertexIndices[0] = GetAttribute(reader, "v1"); - NewFace.VertexIndices[1] = GetAttribute(reader, "v2"); - NewFace.VertexIndices[2] = GetAttribute(reader, "v3"); - - /// @todo Support quads - if (!quadWarned && reader->getAttributeValue("v4")) { - DefaultLogger::get()->warn("Submesh has quads, only triangles are supported at the moment!"); - } - - submesh.Faces.push_back(NewFace); - - // Advance - NextNode(reader); - currentNodeName = reader->getNodeName(); - } - - if (submesh.Faces.size() == numFaces) - { - DefaultLogger::get()->debug(Formatter::format() << " - Faces " << numFaces); - } - else - { - throw DeadlyImportError(Formatter::format() << "Read only " << submesh.Faces.size() << " faces when should have read " << numFaces); - } - } - else if (currentNodeName == nnGeometry) - { - unsigned int numVertices = GetAttribute(reader, "vertexcount"); - - NextNode(reader); - while(string(reader->getNodeName()) == nnVertexBuffer) { - ReadVertexBuffer(submesh, reader, numVertices); - } - } - else if (reader->getNodeName() == nnBoneAssignments) - { - ReadBoneWeights(submesh, reader); - } - - currentNodeName = reader->getNodeName(); - } -} - -void OgreImporter::ReadVertexBuffer(SubMesh &submesh, XmlReader *reader, const unsigned int numVertices) -{ - DefaultLogger::get()->debug(Formatter::format() << "Reading vertex buffer with " << numVertices << " vertices"); - - submesh.HasGeometry = true; - - if (reader->getAttributeValue("positions") && GetAttribute(reader, "positions")) - { - submesh.HasPositions = true; - submesh.Positions.reserve(numVertices); - DefaultLogger::get()->debug(" - Has positions"); - } - if (reader->getAttributeValue("normals") && GetAttribute(reader, "normals")) - { - submesh.HasNormals = true; - submesh.Normals.reserve(numVertices); - DefaultLogger::get()->debug(" - Has normals"); - } - if (reader->getAttributeValue("tangents") && GetAttribute(reader, "tangents")) - { - submesh.HasTangents = true; - submesh.Tangents.reserve(numVertices); - DefaultLogger::get()->debug(" - Has tangents"); - } - if (reader->getAttributeValue("texture_coords")) - { - submesh.Uvs.resize(GetAttribute(reader, "texture_coords")); - for(size_t i=0, len=submesh.Uvs.size(); idebug(Formatter::format() << " - Has " << submesh.Uvs.size() << " texture coords"); - } - - if (!submesh.HasPositions) { - throw DeadlyImportError("Vertex buffer does not contain positions!"); - } - - const string nnVertex = "vertex"; - const string nnPosition = "position"; - const string nnNormal = "normal"; - const string nnTangent = "tangent"; - const string nnBinormal = "binormal"; - const string nnTexCoord = "texcoord"; - const string nnColorDiffuse = "colour_diffuse"; - const string nnColorSpecular = "colour_specular"; - - bool warnBinormal = true; - bool warnColorDiffuse = true; - bool warnColorSpecular = true; - - NextNode(reader); - string currentNodeName = reader->getNodeName(); - - /// @todo Make this loop nicer. - while(currentNodeName == nnVertex || - currentNodeName == nnPosition || - currentNodeName == nnNormal || - currentNodeName == nnTangent || - currentNodeName == nnBinormal || - currentNodeName == nnTexCoord || - currentNodeName == nnColorDiffuse || - currentNodeName == nnColorSpecular) - { - if (currentNodeName == nnVertex) - { - NextNode(reader); - currentNodeName = reader->getNodeName(); - } - - /// @todo Implement nnBinormal, nnColorDiffuse and nnColorSpecular - - if (submesh.HasPositions && currentNodeName == nnPosition) - { - aiVector3D NewPos; - NewPos.x = GetAttribute(reader, "x"); - NewPos.y = GetAttribute(reader, "y"); - NewPos.z = GetAttribute(reader, "z"); - submesh.Positions.push_back(NewPos); - } - else if (submesh.HasNormals && currentNodeName == nnNormal) - { - aiVector3D NewNormal; - NewNormal.x = GetAttribute(reader, "x"); - NewNormal.y = GetAttribute(reader, "y"); - NewNormal.z = GetAttribute(reader, "z"); - submesh.Normals.push_back(NewNormal); - } - else if (submesh.HasTangents && currentNodeName == nnTangent) - { - aiVector3D NewTangent; - NewTangent.x = GetAttribute(reader, "x"); - NewTangent.y = GetAttribute(reader, "y"); - NewTangent.z = GetAttribute(reader, "z"); - submesh.Tangents.push_back(NewTangent); - } - else if (submesh.Uvs.size() > 0 && currentNodeName == nnTexCoord) - { - for(size_t i=0, len=submesh.Uvs.size(); i(reader, "u"); - NewUv.y = GetAttribute(reader, "v") * (-1)+1; //flip the uv vertikal, blender exports them so! (ahem... @todo ????) - submesh.Uvs[i].push_back(NewUv); - - NextNode(reader); - currentNodeName = reader->getNodeName(); - } - // Continue main loop as above already read next node - continue; - } - else - { - /// @todo Remove this stuff once implemented. We only want to log warnings once per element. - bool warn = true; - if (currentNodeName == nnBinormal) - { - if (warnBinormal) - { - warnBinormal = false; - } - else - { - warn = false; - } - } - else if (currentNodeName == nnColorDiffuse) - { - if (warnColorDiffuse) - { - warnColorDiffuse = false; - } - else - { - warn = false; - } - } - else if (currentNodeName == nnColorSpecular) - { - if (warnColorSpecular) - { - warnColorSpecular = false; - } - else - { - warn = false; - } - } - if (warn) { - DefaultLogger::get()->warn(string("Vertex buffer attribute read not implemented for element: ") + currentNodeName); - } - } - - // Advance - NextNode(reader); - currentNodeName = reader->getNodeName(); - } - - DefaultLogger::get()->debug(Formatter::format() << - " - Positions " << submesh.Positions.size() << - " Normals " << submesh.Normals.size() << - " TexCoords " << submesh.Uvs.size() << - " Tangents " << submesh.Tangents.size()); - - // Sanity checks - if (submesh.HasNormals && submesh.Normals.size() != numVertices) { - throw DeadlyImportError(Formatter::format() << "Read only " << submesh.Normals.size() << " normals when should have read " << numVertices); - } - if (submesh.HasTangents && submesh.Tangents.size() != numVertices) { - throw DeadlyImportError(Formatter::format() << "Read only " << submesh.Tangents.size() << " tangents when should have read " << numVertices); - } - for(unsigned int i=0; i(reader, "boneindex"); - weight.Value = GetAttribute(reader, "weight"); - - //calculate the number of bones used (this is the highest id +1 becuase bone ids start at 0) - /// @todo This can probably be refactored to something else. - submesh.BonesUsed = max(submesh.BonesUsed, weight.Id+1); - - const unsigned int vertexId = GetAttribute(reader, "vertexindex"); - submesh.Weights[vertexId].push_back(weight); - - NextNode(reader); - } - DefaultLogger::get()->debug(Formatter::format() << " - Bone weights " << numRead); -} - -void OgreImporter::ProcessSubMesh(SubMesh &submesh, SubMesh &sharedGeometry) -{ - // Make all vertexes unique. Required by Assimp. - vector uniqueFaceList(submesh.Faces.size()); - unsigned int uniqueVertexCount = submesh.Faces.size() * 3; - - vector uniquePositions(uniqueVertexCount); - vector uniqueNormals(uniqueVertexCount); - vector uniqueTangents(uniqueVertexCount); - - vector > uniqueWeights(uniqueVertexCount); - vector > uniqueUvs(submesh.UseSharedGeometry ? sharedGeometry.Uvs.size() : submesh.Uvs.size()); - - for(size_t uvi=0; uvi &uv = vertexSource.Uvs[uvi]; - uniqueUvs[uvi][pos] = uv[v1]; - uniqueUvs[uvi][pos+1] = uv[v2]; - uniqueUvs[uvi][pos+2] = uv[v3]; - } - - if (!vertexSource.Weights.empty()) - { - uniqueWeights[pos] = vertexSource.Weights[v1]; - uniqueWeights[pos+1] = vertexSource.Weights[v2]; - uniqueWeights[pos+2] = vertexSource.Weights[v3]; - } - } - - // Now we have the unique data, but want them in the SubMesh, so we swap all the containers. - // If we don't have one of them, we just swap empty containers, so everything is ok. - submesh.Faces.swap(uniqueFaceList); - submesh.Positions.swap(uniquePositions); - submesh.Normals.swap(uniqueNormals); - submesh.Tangents.swap(uniqueTangents); - submesh.Uvs.swap(uniqueUvs); - submesh.Weights.swap(uniqueWeights); - - // Normalize bone weights - // For example the Blender exporter doesn't care about whether the sum of all bone - // weights for a single vertex equals 1 or not, so validate here. - for(size_t vertexId=0, wlen=submesh.Weights.size(); vertexId &weights = submesh.Weights[vertexId]; - - float sum = 0.0f; - for(size_t boneId=0, blen=weights.size(); boneId (1.0f + 0.05f))) - { - for(size_t boneId=0, blen=weights.size(); boneId& bones) const -{ - const size_t sizeVector3D = sizeof(aiVector3D); - - aiMesh *dest = new aiMesh(); - - // Material - dest->mMaterialIndex = submesh.MaterialIndex; - - // Positions - dest->mVertices = new aiVector3D[submesh.Positions.size()]; - dest->mNumVertices = submesh.Positions.size(); - memcpy(dest->mVertices, &submesh.Positions[0], submesh.Positions.size() * sizeVector3D); - - // Normals - if (submesh.HasNormals) - { - dest->mNormals = new aiVector3D[submesh.Normals.size()]; - memcpy(dest->mNormals, &submesh.Normals[0], submesh.Normals.size() * sizeVector3D); - } - - // Tangents - // Until we have support for bitangents, no tangents will be written - /// @todo Investigate why the above? - if (submesh.HasTangents) - { - DefaultLogger::get()->warn("Tangents found from Ogre mesh but writing to Assimp mesh not yet supported!"); - //dest->mTangents = new aiVector3D[submesh.Tangents.size()]; - //memcpy(dest->mTangents, &submesh.Tangents[0], submesh.Tangents.size() * sizeVector3D); - } - - // UVs - for (size_t i=0, len=submesh.Uvs.size(); imNumUVComponents[i] = 2; - dest->mTextureCoords[i] = new aiVector3D[submesh.Uvs[i].size()]; - memcpy(dest->mTextureCoords[i], &(submesh.Uvs[i][0]), submesh.Uvs[i].size() * sizeVector3D); - } - - // Bone weights. Convert internal vertex-to-bone mapping to bone-to-vertex. - vector > assimpWeights(submesh.BonesUsed); - for(size_t vertexId=0, len=submesh.Weights.size(); vertexId &vertexWeights = submesh.Weights[vertexId]; - for (size_t boneId=0, len=vertexWeights.size(); boneId assimpBones; - assimpBones.reserve(submesh.BonesUsed); - - for(size_t boneId=0, len=submesh.BonesUsed; boneId &boneWeights = assimpWeights[boneId]; - if (boneWeights.size() == 0) { - continue; - } - - // @note The bones list is sorted by id's, this was done in LoadSkeleton. - aiBone *assimpBone = new aiBone(); - assimpBone->mName = bones[boneId].Name; - assimpBone->mOffsetMatrix = bones[boneId].BoneToWorldSpace; - assimpBone->mNumWeights = boneWeights.size(); - assimpBone->mWeights = new aiVertexWeight[boneWeights.size()]; - memcpy(assimpBone->mWeights, &boneWeights[0], boneWeights.size() * sizeof(aiVertexWeight)); - - assimpBones.push_back(assimpBone); - } - - if (!assimpBones.empty()) - { - dest->mBones = new aiBone*[assimpBones.size()]; - dest->mNumBones = assimpBones.size(); - - for(size_t i=0, len=assimpBones.size(); imBones[i] = assimpBones[i]; - } - } - - // Faces - dest->mFaces = new aiFace[submesh.Faces.size()]; - dest->mNumFaces = submesh.Faces.size(); - - for(size_t i=0, len=submesh.Faces.size(); imFaces[i].mNumIndices = 3; - dest->mFaces[i].mIndices = new unsigned int[3]; - - const Face &f = submesh.Faces[i]; - dest->mFaces[i].mIndices[0] = f.VertexIndices[0]; - dest->mFaces[i].mIndices[1] = f.VertexIndices[1]; - dest->mFaces[i].mIndices[2] = f.VertexIndices[2]; - } - - return dest; -} - -} // Ogre -} // Assimp - -#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER diff --git a/code/OgreParsingUtils.h b/code/OgreParsingUtils.h index ac1e58173..d3a7aa8bf 100644 --- a/code/OgreParsingUtils.h +++ b/code/OgreParsingUtils.h @@ -1,3 +1,42 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ #ifndef AI_OGREPARSINGUTILS_H_INC #define AI_OGREPARSINGUTILS_H_INC @@ -5,144 +44,13 @@ #ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER #include "ParsingUtils.h" -#include "irrXMLWrapper.h" -#include "fast_atof.h" #include + namespace Assimp { namespace Ogre { -typedef irr::io::IrrXMLReader XmlReader; - -static void ThrowAttibuteError(const XmlReader* reader, const std::string &name, const std::string &error = "") -{ - if (!error.empty()) - { - throw DeadlyImportError(error + " in node '" + std::string(reader->getNodeName()) + "' and attribute '" + name + "'"); - } - else - { - throw DeadlyImportError("Attribute '" + name + "' does not exist in node '" + std::string(reader->getNodeName()) + "'"); - } -} - -template -inline T GetAttribute(const XmlReader* reader, const std::string &name); - -template<> -inline int GetAttribute(const XmlReader* reader, const std::string &name) -{ - const char* value = reader->getAttributeValue(name.c_str()); - if (value) - { - return atoi(value); - } - else - { - ThrowAttibuteError(reader, name); - return 0; - } -} - -template<> -inline unsigned int GetAttribute(const XmlReader* reader, const std::string &name) -{ - const char* value = reader->getAttributeValue(name.c_str()); - if (value) - { - return static_cast(atoi(value)); ///< @todo Find a better way... - } - else - { - ThrowAttibuteError(reader, name); - return 0; - } -} - -template<> -inline float GetAttribute(const XmlReader* reader, const std::string &name) -{ - const char* value = reader->getAttributeValue(name.c_str()); - if (value) - { - return fast_atof(value); - } - else - { - ThrowAttibuteError(reader, name); - return 0.f; - } -} - -template<> -inline std::string GetAttribute(const XmlReader* reader, const std::string &name) -{ - const char* value = reader->getAttributeValue(name.c_str()); - if (value) - { - return std::string(value); - } - else - { - ThrowAttibuteError(reader, name); - return ""; - } -} - -template<> -inline bool GetAttribute(const XmlReader* reader, const std::string &name) -{ - std::string value = GetAttribute(reader, name); - if (ASSIMP_stricmp(value, "true") == 0) - { - return true; - } - else if (ASSIMP_stricmp(value, "false") == 0) - { - return false; - } - else - { - ThrowAttibuteError(reader, name, "Boolean value is expected to be 'true' or 'false', encountered '" + value + "'"); - return false; - } -} - -inline bool NextNode(XmlReader* reader) -{ - do - { - if (!reader->read()) { - return false; - } - } - while(reader->getNodeType() != irr::io::EXN_ELEMENT); - return true; -} - -inline bool CurrentNodeNameEquals(const XmlReader* reader, const std::string &name) -{ - return (ASSIMP_stricmp(std::string(reader->getNodeName()), name) == 0); -} - -/// Skips a line from current @ss position until a newline. Returns the skipped part. -static inline std::string SkipLine(std::stringstream &ss) -{ - std::string skipped; - getline(ss, skipped); - return skipped; -} - -/// Skips a line and reads next element from @c ss to @c nextElement. -/** @return Skipped line content until newline. */ -static inline std::string NextAfterNewLine(std::stringstream &ss, std::string &nextElement) -{ - std::string skipped = SkipLine(ss); - ss >> nextElement; - return skipped; -} - /// Returns a lower cased copy of @s. static inline std::string ToLower(std::string s) { @@ -207,6 +115,23 @@ static inline std::string &Trim(std::string &s, bool newlines = true) return TrimLeft(TrimRight(s, newlines), newlines); } +/// Skips a line from current @ss position until a newline. Returns the skipped part. +static inline std::string SkipLine(std::stringstream &ss) +{ + std::string skipped; + getline(ss, skipped); + return skipped; +} + +/// Skips a line and reads next element from @c ss to @c nextElement. +/** @return Skipped line content until newline. */ +static inline std::string NextAfterNewLine(std::stringstream &ss, std::string &nextElement) +{ + std::string skipped = SkipLine(ss); + ss >> nextElement; + return skipped; +} + } // Ogre } // Assimp diff --git a/code/OgreSkeleton.cpp b/code/OgreSkeleton.cpp deleted file mode 100644 index 3f0b7abb1..000000000 --- a/code/OgreSkeleton.cpp +++ /dev/null @@ -1,446 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2012, assimp team -All rights reserved. - -Redistribution and use of this software in aSource and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of aSource code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -#include "AssimpPCH.h" - -#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER - -#include "OgreImporter.h" -#include "TinyFormatter.h" - -using namespace std; - -namespace Assimp -{ -namespace Ogre -{ - -void OgreImporter::ReadSkeleton(const std::string &pFile, Assimp::IOSystem *pIOHandler, const aiScene *pScene, - const std::string &skeletonFile, vector &Bones, vector &Animations) const -{ - string filename = skeletonFile; - if (EndsWith(filename, ".skeleton")) - { - DefaultLogger::get()->warn("Mesh is referencing a Ogre binary skeleton. Parsing binary Ogre assets is not supported at the moment. Trying to find .skeleton.xml file instead."); - filename += ".xml"; - } - - if (!pIOHandler->Exists(filename)) - { - DefaultLogger::get()->error("Failed to find skeleton file '" + filename + "', skeleton will be missing."); - return; - } - - boost::scoped_ptr file(pIOHandler->Open(filename)); - if (!file.get()) { - throw DeadlyImportError("Failed to open skeleton file " + filename); - } - - boost::scoped_ptr stream(new CIrrXML_IOStreamReader(file.get())); - XmlReader* reader = irr::io::createIrrXMLReader(stream.get()); - if (!reader) { - throw DeadlyImportError("Failed to create XML reader for skeleton file " + filename); - } - - DefaultLogger::get()->debug("Reading skeleton '" + filename + "'"); - - // Root - NextNode(reader); - if (!CurrentNodeNameEquals(reader, "skeleton")) { - throw DeadlyImportError("Root node is not but <" + string(reader->getNodeName()) + "> in " + filename); - } - - // Bones - NextNode(reader); - if (!CurrentNodeNameEquals(reader, "bones")) { - throw DeadlyImportError("No node in skeleton " + skeletonFile); - } - - NextNode(reader); - while(CurrentNodeNameEquals(reader, "bone")) - { - /** @todo Fix this mandatory ordering. Some exporters might just write rotation first etc. - There is no technical reason this has to be so strict. */ - - Bone bone; - bone.Id = GetAttribute(reader, "id"); - bone.Name = GetAttribute(reader, "name"); - - NextNode(reader); - if (!CurrentNodeNameEquals(reader, "position")) { - throw DeadlyImportError("Position is not first node in Bone!"); - } - - bone.Position.x = GetAttribute(reader, "x"); - bone.Position.y = GetAttribute(reader, "y"); - bone.Position.z = GetAttribute(reader, "z"); - - NextNode(reader); - if (!CurrentNodeNameEquals(reader, "rotation")) { - throw DeadlyImportError("Rotation is not the second node in Bone!"); - } - - bone.RotationAngle = GetAttribute(reader, "angle"); - - NextNode(reader); - if (!CurrentNodeNameEquals(reader, "axis")) { - throw DeadlyImportError("No axis specified for bone rotation!"); - } - - bone.RotationAxis.x = GetAttribute(reader, "x"); - bone.RotationAxis.y = GetAttribute(reader, "y"); - bone.RotationAxis.z = GetAttribute(reader, "z"); - - Bones.push_back(bone); - - NextNode(reader); - } - - // Order bones by Id - std::sort(Bones.begin(), Bones.end()); - - // Validate that bone indexes are not skipped. - /** @note Left this from original authors code, but not sure if this is strictly necessary - as per the Ogre skeleton spec. It might be more that other (later) code in this imported does not break. */ - for (size_t i=0, len=Bones.size(); i(Bones[i].Id) != static_cast(i)) { - throw DeadlyImportError("Bone Ids are not in sequence in " + skeletonFile); - } - } - - DefaultLogger::get()->debug(Formatter::format() << " - Bones " << Bones.size()); - - // Bone hierarchy - if (!CurrentNodeNameEquals(reader, "bonehierarchy")) { - throw DeadlyImportError("No node found after in " + skeletonFile); - } - - NextNode(reader); - while(CurrentNodeNameEquals(reader, "boneparent")) - { - string childName = GetAttribute(reader, "bone"); - string parentName = GetAttribute(reader, "parent"); - - vector::iterator iterChild = find(Bones.begin(), Bones.end(), childName); - vector::iterator iterParent = find(Bones.begin(), Bones.end(), parentName); - - if (iterChild != Bones.end() && iterParent != Bones.end()) - { - iterChild->ParentId = iterParent->Id; - iterParent->Children.push_back(iterChild->Id); - } - else - { - DefaultLogger::get()->warn("Failed to find bones for parenting: Child " + childName + " Parent " + parentName); - } - - NextNode(reader); - } - - // Calculate bone matrices for root bones. Recursively does their children. - BOOST_FOREACH(Bone &theBone, Bones) - { - if (!theBone.IsParented()) { - theBone.CalculateBoneToWorldSpaceMatrix(Bones); - } - } - - aiVector3D zeroVec(0.f, 0.f, 0.f); - - // Animations - if (CurrentNodeNameEquals(reader, "animations")) - { - DefaultLogger::get()->debug(" - Animations"); - - NextNode(reader); - while(CurrentNodeNameEquals(reader, "animation")) - { - Animation animation; - animation.Name = GetAttribute(reader, "name"); - animation.Length = GetAttribute(reader, "length"); - - // Tracks - NextNode(reader); - if (!CurrentNodeNameEquals(reader, "tracks")) { - throw DeadlyImportError("No node found in animation '" + animation.Name + "' in " + skeletonFile); - } - - NextNode(reader); - while(CurrentNodeNameEquals(reader, "track")) - { - Track track; - track.BoneName = GetAttribute(reader, "bone"); - - // Keyframes - NextNode(reader); - if (!CurrentNodeNameEquals(reader, "keyframes")) { - throw DeadlyImportError("No node found in a track in animation '" + animation.Name + "' in " + skeletonFile); - } - - NextNode(reader); - while(CurrentNodeNameEquals(reader, "keyframe")) - { - KeyFrame keyFrame; - keyFrame.Time = GetAttribute(reader, "time"); - - NextNode(reader); - while(CurrentNodeNameEquals(reader, "translate") || CurrentNodeNameEquals(reader, "rotate") || CurrentNodeNameEquals(reader, "scale")) - { - if (CurrentNodeNameEquals(reader, "translate")) - { - keyFrame.Position.x = GetAttribute(reader, "x"); - keyFrame.Position.y = GetAttribute(reader, "y"); - keyFrame.Position.z = GetAttribute(reader, "z"); - } - else if (CurrentNodeNameEquals(reader, "rotate")) - { - float angle = GetAttribute(reader, "angle"); - - NextNode(reader); - if (!CurrentNodeNameEquals(reader, "axis")) { - throw DeadlyImportError("No axis for keyframe rotation in animation '" + animation.Name + "'"); - } - - aiVector3D axis; - axis.x = GetAttribute(reader, "x"); - axis.y = GetAttribute(reader, "y"); - axis.z = GetAttribute(reader, "z"); - - if (axis.Equal(zeroVec)) - { - axis.x = 1.0f; - if (angle != 0) { - DefaultLogger::get()->warn("Found invalid a key frame with a zero rotation axis in animation '" + animation.Name + "'"); - } - } - keyFrame.Rotation = aiQuaternion(axis, angle); - } - else if (CurrentNodeNameEquals(reader, "scale")) - { - keyFrame.Scaling.x = GetAttribute(reader, "x"); - keyFrame.Scaling.y = GetAttribute(reader, "y"); - keyFrame.Scaling.z = GetAttribute(reader, "z"); - } - NextNode(reader); - } - track.Keyframes.push_back(keyFrame); - } - animation.Tracks.push_back(track); - } - Animations.push_back(animation); - - DefaultLogger::get()->debug(Formatter::format() << " " << animation.Name << " (" << animation.Length << " sec, " << animation.Tracks.size() << " tracks)"); - } - } -} - -void OgreImporter::CreateAssimpSkeleton(aiScene *pScene, const std::vector &bones, const std::vector &animations) -{ - if (bones.empty()) { - return; - } - - if (!pScene->mRootNode) { - throw DeadlyImportError("Creating Assimp skeleton: No root node created!"); - } - if (pScene->mRootNode->mNumChildren > 0) { - throw DeadlyImportError("Creating Assimp skeleton: Root node already has children!"); - } - - // Bones - vector rootBones; - BOOST_FOREACH(const Bone &bone, bones) - { - if (!bone.IsParented()) { - rootBones.push_back(CreateNodeFromBone(bone.Id, bones, pScene->mRootNode)); - } - } - - if (!rootBones.empty()) - { - pScene->mRootNode->mChildren = new aiNode*[rootBones.size()]; - pScene->mRootNode->mNumChildren = rootBones.size(); - - for(size_t i=0, len=rootBones.size(); imRootNode->mChildren[i] = rootBones[i]; - } - } - - // TODO: Auf nicht vorhandene Animationskeys achten! - // @todo Pay attention to non-existing animation Keys (google translated from above german comment) - - // Animations - if (!animations.empty()) - { - pScene->mAnimations = new aiAnimation*[animations.size()]; - pScene->mNumAnimations = animations.size(); - - for(size_t ai=0, alen=animations.size(); aimName = aSource.Name; - animation->mDuration = aSource.Length; - animation->mTicksPerSecond = 1.0f; - - // Tracks - animation->mChannels = new aiNodeAnim*[aSource.Tracks.size()]; - animation->mNumChannels = aSource.Tracks.size(); - - for(size_t ti=0, tlen=aSource.Tracks.size(); timNodeName = tSource.BoneName; - - // We need this, to access the bones default pose. - // Which we need to make keys absolute to the default bone pose. - vector::const_iterator boneIter = find(bones.begin(), bones.end(), tSource.BoneName); - if (boneIter == bones.end()) - { - for(size_t createdAnimationIndex=0; createdAnimationIndexmAnimations[createdAnimationIndex]; - } - delete [] pScene->mAnimations; - pScene->mAnimations = NULL; - pScene->mNumAnimations = 0; - - DefaultLogger::get()->error("Failed to find bone for name " + tSource.BoneName + " when creating animation " + aSource.Name + - ". This is a serious error, animations wont be imported."); - return; - } - - aiMatrix4x4 t0, t1; - aiMatrix4x4 defaultBonePose = aiMatrix4x4::Translation(boneIter->Position, t1) * aiMatrix4x4::Rotation(boneIter->RotationAngle, boneIter->RotationAxis, t0); - - // Keyframes - unsigned int numKeyframes = tSource.Keyframes.size(); - - animationNode->mPositionKeys = new aiVectorKey[numKeyframes]; - animationNode->mRotationKeys = new aiQuatKey[numKeyframes]; - animationNode->mScalingKeys = new aiVectorKey[numKeyframes]; - animationNode->mNumPositionKeys = numKeyframes; - animationNode->mNumRotationKeys = numKeyframes; - animationNode->mNumScalingKeys = numKeyframes; - - //...and fill them - for(size_t kfi=0; kfimPositionKeys[kfi].mTime = static_cast(kfSource.Time); - animationNode->mRotationKeys[kfi].mTime = static_cast(kfSource.Time); - animationNode->mScalingKeys[kfi].mTime = static_cast(kfSource.Time); - - animationNode->mPositionKeys[kfi].mValue = kfPos; - animationNode->mRotationKeys[kfi].mValue = kfRot; - animationNode->mScalingKeys[kfi].mValue = kfScale; - } - animation->mChannels[ti] = animationNode; - } - pScene->mAnimations[ai] = animation; - } - } -} - -aiNode* OgreImporter::CreateNodeFromBone(int boneId, const std::vector &bones, aiNode* parent) -{ - aiMatrix4x4 t0,t1; - const Bone &source = bones[boneId]; - - aiNode* boneNode = new aiNode(source.Name); - boneNode->mParent = parent; - boneNode->mTransformation = aiMatrix4x4::Translation(source.Position, t0) * aiMatrix4x4::Rotation(source.RotationAngle, source.RotationAxis, t1); - - if (!source.Children.empty()) - { - boneNode->mChildren = new aiNode*[source.Children.size()]; - boneNode->mNumChildren = source.Children.size(); - - for(size_t i=0, len=source.Children.size(); imChildren[i] = CreateNodeFromBone(source.Children[i], bones, boneNode); - } - } - - return boneNode; -} - -void Bone::CalculateBoneToWorldSpaceMatrix(vector &Bones) -{ - aiMatrix4x4 t0, t1; - aiMatrix4x4 transform = aiMatrix4x4::Rotation(-RotationAngle, RotationAxis, t1) * aiMatrix4x4::Translation(-Position, t0); - - if (!IsParented()) - { - BoneToWorldSpace = transform; - } - else - { - BoneToWorldSpace = transform * Bones[ParentId].BoneToWorldSpace; - } - - // Recursively for all children now that the parent matrix has been calculated. - BOOST_FOREACH(int childId, Children) - { - Bones[childId].CalculateBoneToWorldSpaceMatrix(Bones); - } -} - -} // Ogre -} // Assimp - -#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER diff --git a/code/OgreStructs.cpp b/code/OgreStructs.cpp new file mode 100644 index 000000000..3eaf2df01 --- /dev/null +++ b/code/OgreStructs.cpp @@ -0,0 +1,1193 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER + +#include "OgreStructs.h" +#include "TinyFormatter.h" + +namespace Assimp +{ +namespace Ogre +{ + +// VertexElement + +VertexElement::VertexElement() : + index(0), + source(0), + offset(0), + type(VET_FLOAT1), + semantic(VES_POSITION) +{ +} + +size_t VertexElement::Size() const +{ + return TypeSize(type); +} + +size_t VertexElement::ComponentCount() const +{ + return ComponentCount(type); +} + +size_t VertexElement::ComponentCount(Type type) +{ + switch(type) + { + case VET_COLOUR: + case VET_COLOUR_ABGR: + case VET_COLOUR_ARGB: + case VET_FLOAT1: + case VET_DOUBLE1: + case VET_SHORT1: + case VET_USHORT1: + case VET_INT1: + case VET_UINT1: + return 1; + case VET_FLOAT2: + case VET_DOUBLE2: + case VET_SHORT2: + case VET_USHORT2: + case VET_INT2: + case VET_UINT2: + return 2; + case VET_FLOAT3: + case VET_DOUBLE3: + case VET_SHORT3: + case VET_USHORT3: + case VET_INT3: + case VET_UINT3: + return 3; + case VET_FLOAT4: + case VET_DOUBLE4: + case VET_SHORT4: + case VET_USHORT4: + case VET_INT4: + case VET_UINT4: + case VET_UBYTE4: + return 4; + } + return 0; +} + +size_t VertexElement::TypeSize(Type type) +{ + switch(type) + { + case VET_COLOUR: + case VET_COLOUR_ABGR: + case VET_COLOUR_ARGB: + return sizeof(unsigned int); + case VET_FLOAT1: + return sizeof(float); + case VET_FLOAT2: + return sizeof(float)*2; + case VET_FLOAT3: + return sizeof(float)*3; + case VET_FLOAT4: + return sizeof(float)*4; + case VET_DOUBLE1: + return sizeof(double); + case VET_DOUBLE2: + return sizeof(double)*2; + case VET_DOUBLE3: + return sizeof(double)*3; + case VET_DOUBLE4: + return sizeof(double)*4; + case VET_SHORT1: + return sizeof(short); + case VET_SHORT2: + return sizeof(short)*2; + case VET_SHORT3: + return sizeof(short)*3; + case VET_SHORT4: + return sizeof(short)*4; + case VET_USHORT1: + return sizeof(unsigned short); + case VET_USHORT2: + return sizeof(unsigned short)*2; + case VET_USHORT3: + return sizeof(unsigned short)*3; + case VET_USHORT4: + return sizeof(unsigned short)*4; + case VET_INT1: + return sizeof(int); + case VET_INT2: + return sizeof(int)*2; + case VET_INT3: + return sizeof(int)*3; + case VET_INT4: + return sizeof(int)*4; + case VET_UINT1: + return sizeof(unsigned int); + case VET_UINT2: + return sizeof(unsigned int)*2; + case VET_UINT3: + return sizeof(unsigned int)*3; + case VET_UINT4: + return sizeof(unsigned int)*4; + case VET_UBYTE4: + return sizeof(unsigned char)*4; + } + return 0; +} + +std::string VertexElement::TypeToString() +{ + return TypeToString(type); +} + +std::string VertexElement::TypeToString(Type type) +{ + switch(type) + { + case VET_COLOUR: return "COLOUR"; + case VET_COLOUR_ABGR: return "COLOUR_ABGR"; + case VET_COLOUR_ARGB: return "COLOUR_ARGB"; + case VET_FLOAT1: return "FLOAT1"; + case VET_FLOAT2: return "FLOAT2"; + case VET_FLOAT3: return "FLOAT3"; + case VET_FLOAT4: return "FLOAT4"; + case VET_DOUBLE1: return "DOUBLE1"; + case VET_DOUBLE2: return "DOUBLE2"; + case VET_DOUBLE3: return "DOUBLE3"; + case VET_DOUBLE4: return "DOUBLE4"; + case VET_SHORT1: return "SHORT1"; + case VET_SHORT2: return "SHORT2"; + case VET_SHORT3: return "SHORT3"; + case VET_SHORT4: return "SHORT4"; + case VET_USHORT1: return "USHORT1"; + case VET_USHORT2: return "USHORT2"; + case VET_USHORT3: return "USHORT3"; + case VET_USHORT4: return "USHORT4"; + case VET_INT1: return "INT1"; + case VET_INT2: return "INT2"; + case VET_INT3: return "INT3"; + case VET_INT4: return "INT4"; + case VET_UINT1: return "UINT1"; + case VET_UINT2: return "UINT2"; + case VET_UINT3: return "UINT3"; + case VET_UINT4: return "UINT4"; + case VET_UBYTE4: return "UBYTE4"; + } + return "Uknown_VertexElement::Type"; +} + +std::string VertexElement::SemanticToString() +{ + return SemanticToString(semantic); +} + +std::string VertexElement::SemanticToString(Semantic semantic) +{ + switch(semantic) + { + case VES_POSITION: return "POSITION"; + case VES_BLEND_WEIGHTS: return "BLEND_WEIGHTS"; + case VES_BLEND_INDICES: return "BLEND_INDICES"; + case VES_NORMAL: return "NORMAL"; + case VES_DIFFUSE: return "DIFFUSE"; + case VES_SPECULAR: return "SPECULAR"; + case VES_TEXTURE_COORDINATES: return "TEXTURE_COORDINATES"; + case VES_BINORMAL: return "BINORMAL"; + case VES_TANGENT: return "TANGENT"; + } + return "Uknown_VertexElement::Semantic"; +} + +// IVertexData + +IVertexData::IVertexData() : + count(0) +{ +} + +bool IVertexData::HasBoneAssignments() const +{ + return !boneAssignments.empty(); +} + +void IVertexData::AddVertexMapping(uint32_t oldIndex, uint32_t newIndex) +{ + BoneAssignmentsForVertex(oldIndex, newIndex, boneAssignmentsMap[newIndex]); + vertexIndexMapping[oldIndex].push_back(newIndex); +} + +void IVertexData::BoneAssignmentsForVertex(uint32_t currentIndex, uint32_t newIndex, VertexBoneAssignmentList &dest) const +{ + for (VertexBoneAssignmentList::const_iterator iter=boneAssignments.begin(), end=boneAssignments.end(); + iter!=end; ++iter) + { + if (iter->vertexIndex == currentIndex) + { + VertexBoneAssignment a = (*iter); + a.vertexIndex = newIndex; + dest.push_back(a); + } + } +} + +AssimpVertexBoneWeightList IVertexData::AssimpBoneWeights(size_t vertices) +{ + AssimpVertexBoneWeightList weights; + for(size_t vi=0; vi &boneWeights = weights[iter->boneIndex]; + boneWeights.push_back(aiVertexWeight(vi, iter->weight)); + } + } + return weights; +} + +std::set IVertexData::ReferencedBonesByWeights() const +{ + std::set referenced; + for (VertexBoneAssignmentList::const_iterator iter=boneAssignments.begin(), end=boneAssignments.end(); + iter!=end; ++iter) + { + referenced.insert(iter->boneIndex); + } + return referenced; +} + +// VertexData + +VertexData::VertexData() +{ +} + +VertexData::~VertexData() +{ + Reset(); +} + +void VertexData::Reset() +{ + // Releases shared ptr memory streams. + vertexBindings.clear(); + vertexElements.clear(); +} + +uint32_t VertexData::VertexSize(uint16_t source) const +{ + uint32_t size = 0; + for(VertexElementList::const_iterator iter=vertexElements.begin(), end=vertexElements.end(); iter != end; ++iter) + { + if (iter->source == source) + size += iter->Size(); + } + return size; +} + +MemoryStream *VertexData::VertexBuffer(uint16_t source) +{ + if (vertexBindings.find(source) != vertexBindings.end()) + return vertexBindings[source].get(); + return 0; +} + +VertexElement *VertexData::GetVertexElement(VertexElement::Semantic semantic, uint16_t index) +{ + for(VertexElementList::iterator iter=vertexElements.begin(), end=vertexElements.end(); iter != end; ++iter) + { + VertexElement &element = (*iter); + if (element.semantic == semantic && element.index == index) + return &element; + } + return 0; +} + +// VertexDataXml + +VertexDataXml::VertexDataXml() +{ +} + +bool VertexDataXml::HasPositions() const +{ + return !positions.empty(); +} + +bool VertexDataXml::HasNormals() const +{ + return !normals.empty(); +} + +bool VertexDataXml::HasTangents() const +{ + return !tangents.empty(); +} + +bool VertexDataXml::HasUvs() const +{ + return !uvs.empty(); +} + +size_t VertexDataXml::NumUvs() const +{ + return uvs.size(); +} + +// IndexData + +IndexData::IndexData() : + count(0), + faceCount(0), + is32bit(false) +{ +} + +IndexData::~IndexData() +{ + Reset(); +} + +void IndexData::Reset() +{ + // Release shared ptr memory stream. + buffer.reset(); +} + +size_t IndexData::IndexSize() const +{ + return (is32bit ? sizeof(uint32_t) : sizeof(uint16_t)); +} + +size_t IndexData::FaceSize() const +{ + return IndexSize() * 3; +} + +// Mesh + +Mesh::Mesh() : + sharedVertexData(0), + skeleton(0), + hasSkeletalAnimations(false) +{ +} + +Mesh::~Mesh() +{ + Reset(); +} + +void Mesh::Reset() +{ + OGRE_SAFE_DELETE(skeleton) + OGRE_SAFE_DELETE(sharedVertexData) + + for(size_t i=0, len=subMeshes.size(); iindex == index) + return subMeshes[i]; + return 0; +} + +void Mesh::ConvertToAssimpScene(aiScene* dest) +{ + // Setup + dest->mNumMeshes = NumSubMeshes(); + dest->mMeshes = new aiMesh*[dest->mNumMeshes]; + + // Create root node + dest->mRootNode = new aiNode(); + dest->mRootNode->mNumMeshes = dest->mNumMeshes; + dest->mRootNode->mMeshes = new unsigned int[dest->mRootNode->mNumMeshes]; + + // Export meshes + for(size_t i=0; imNumMeshes; ++i) + { + dest->mMeshes[i] = subMeshes[i]->ConvertToAssimpMesh(this); + dest->mRootNode->mMeshes[i] = i; + } + + // Export skeleton + if (skeleton) + { + // Bones + if (!skeleton->bones.empty()) + { + BoneList rootBones = skeleton->RootBones(); + dest->mRootNode->mNumChildren = rootBones.size(); + dest->mRootNode->mChildren = new aiNode*[dest->mRootNode->mNumChildren]; + + for(size_t i=0, len=rootBones.size(); imRootNode->mChildren[i] = rootBones[i]->ConvertToAssimpNode(skeleton, dest->mRootNode); + } + } + + // Animations + if (!skeleton->animations.empty()) + { + dest->mNumAnimations = skeleton->animations.size(); + dest->mAnimations = new aiAnimation*[dest->mNumAnimations]; + + for(size_t i=0, len=skeleton->animations.size(); imAnimations[i] = skeleton->animations[i]->ConvertToAssimpAnimation(); + } + } + } +} + +// ISubMesh + +ISubMesh::ISubMesh() : + index(0), + materialIndex(-1), + usesSharedVertexData(false), + operationType(OT_POINT_LIST) +{ +} + +// SubMesh + +SubMesh::SubMesh() : + vertexData(0), + indexData(new IndexData()) +{ +} + +SubMesh::~SubMesh() +{ + Reset(); +} + +void SubMesh::Reset() +{ + OGRE_SAFE_DELETE(vertexData) + OGRE_SAFE_DELETE(indexData) +} + +aiMesh *SubMesh::ConvertToAssimpMesh(Mesh *parent) +{ + if (operationType != OT_TRIANGLE_LIST) { + throw DeadlyImportError(Formatter::format() << "Only mesh operation type OT_TRIANGLE_LIST is supported. Found " << operationType); + } + + aiMesh *dest = new aiMesh(); + dest->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + + if (!name.empty()) + dest->mName = name; + + // Material index + if (materialIndex != -1) + dest->mMaterialIndex = materialIndex; + + // Pick source vertex data from shader geometry or from internal geometry. + VertexData *src = (!usesSharedVertexData ? vertexData : parent->sharedVertexData); + + VertexElement *positionsElement = src->GetVertexElement(VertexElement::VES_POSITION); + VertexElement *normalsElement = src->GetVertexElement(VertexElement::VES_NORMAL); + VertexElement *uv1Element = src->GetVertexElement(VertexElement::VES_TEXTURE_COORDINATES, 0); + VertexElement *uv2Element = src->GetVertexElement(VertexElement::VES_TEXTURE_COORDINATES, 1); + + // Sanity checks + if (!positionsElement) { + throw DeadlyImportError("Failed to import Ogre VertexElement::VES_POSITION. Mesh does not have vertex positions!"); + } else if (positionsElement->type != VertexElement::VET_FLOAT3) { + throw DeadlyImportError("Ogre Mesh position vertex element type != VertexElement::VET_FLOAT3. This is not supported."); + } else if (normalsElement && normalsElement->type != VertexElement::VET_FLOAT3) { + throw DeadlyImportError("Ogre Mesh normal vertex element type != VertexElement::VET_FLOAT3. This is not supported."); + } + + // Faces + dest->mNumFaces = indexData->faceCount; + dest->mFaces = new aiFace[dest->mNumFaces]; + + // Assimp required unique vertices, we need to convert from Ogres shared indexing. + size_t uniqueVertexCount = dest->mNumFaces * 3; + dest->mNumVertices = uniqueVertexCount; + dest->mVertices = new aiVector3D[dest->mNumVertices]; + + // Source streams + MemoryStream *positions = src->VertexBuffer(positionsElement->source); + MemoryStream *normals = (normalsElement ? src->VertexBuffer(normalsElement->source) : 0); + MemoryStream *uv1 = (uv1Element ? src->VertexBuffer(uv1Element->source) : 0); + MemoryStream *uv2 = (uv2Element ? src->VertexBuffer(uv2Element->source) : 0); + + // Element size + const size_t sizePosition = positionsElement->Size(); + const size_t sizeNormal = (normalsElement ? normalsElement->Size() : 0); + const size_t sizeUv1 = (uv1Element ? uv1Element->Size() : 0); + const size_t sizeUv2 = (uv2Element ? uv2Element->Size() : 0); + + // Vertex width + const size_t vWidthPosition = src->VertexSize(positionsElement->source); + const size_t vWidthNormal = (normalsElement ? src->VertexSize(normalsElement->source) : 0); + const size_t vWidthUv1 = (uv1Element ? src->VertexSize(uv1Element->source) : 0); + const size_t vWidthUv2 = (uv2Element ? src->VertexSize(uv2Element->source) : 0); + + bool boneAssignments = src->HasBoneAssignments(); + + // Prepare normals + if (normals) + dest->mNormals = new aiVector3D[dest->mNumVertices]; + + // Prepare UVs, ignoring incompatible UVs. + if (uv1) + { + if (uv1Element->type == VertexElement::VET_FLOAT2 || uv1Element->type == VertexElement::VET_FLOAT3) + { + dest->mNumUVComponents[0] = uv1Element->ComponentCount(); + dest->mTextureCoords[0] = new aiVector3D[dest->mNumVertices]; + } + else + { + DefaultLogger::get()->warn(Formatter::format() << "Ogre imported UV0 type " << uv1Element->TypeToString() << " is not compatible with Assimp. Ignoring UV."); + uv1 = 0; + } + } + if (uv2) + { + if (uv2Element->type == VertexElement::VET_FLOAT2 || uv2Element->type == VertexElement::VET_FLOAT3) + { + dest->mNumUVComponents[1] = uv2Element->ComponentCount(); + dest->mTextureCoords[1] = new aiVector3D[dest->mNumVertices]; + } + else + { + DefaultLogger::get()->warn(Formatter::format() << "Ogre imported UV0 type " << uv2Element->TypeToString() << " is not compatible with Assimp. Ignoring UV."); + uv2 = 0; + } + } + + aiVector3D *uv1Dest = (uv1 ? dest->mTextureCoords[0] : 0); + aiVector3D *uv2Dest = (uv2 ? dest->mTextureCoords[1] : 0); + + MemoryStream *faces = indexData->buffer.get(); + for (size_t fi=0, isize=indexData->IndexSize(), fsize=indexData->FaceSize(); + fimNumFaces; ++fi) + { + // Source Ogre face + aiFace ogreFace; + ogreFace.mNumIndices = 3; + ogreFace.mIndices = new unsigned int[3]; + + faces->Seek(fi * fsize, aiOrigin_SET); + if (indexData->is32bit) + { + faces->Read(&ogreFace.mIndices[0], isize, 3); + } + else + { + uint16_t iout = 0; + for (size_t ii=0; ii<3; ++ii) + { + faces->Read(&iout, isize, 1); + ogreFace.mIndices[ii] = static_cast(iout); + } + } + + // Destination Assimp face + aiFace &face = dest->mFaces[fi]; + face.mNumIndices = 3; + face.mIndices = new unsigned int[3]; + + const size_t pos = fi * 3; + for (size_t v=0; v<3; ++v) + { + const size_t newIndex = pos + v; + + // Write face index + face.mIndices[v] = newIndex; + + // Ogres vertex index to ref into the source buffers. + const size_t ogreVertexIndex = ogreFace.mIndices[v]; + src->AddVertexMapping(ogreVertexIndex, newIndex); + + // Position + positions->Seek((vWidthPosition * ogreVertexIndex) + positionsElement->offset, aiOrigin_SET); + positions->Read(&dest->mVertices[newIndex], sizePosition, 1); + + // Normal + if (normals) + { + normals->Seek((vWidthNormal * ogreVertexIndex) + normalsElement->offset, aiOrigin_SET); + normals->Read(&dest->mNormals[newIndex], sizeNormal, 1); + } + // UV0 + if (uv1 && uv1Dest) + { + uv1->Seek((vWidthUv1 * ogreVertexIndex) + uv1Element->offset, aiOrigin_SET); + uv1->Read(&uv1Dest[newIndex], sizeUv1, 1); + uv1Dest[newIndex].y = (uv1Dest[newIndex].y * -1) + 1; // Flip UV from Ogre to Assimp form + } + // UV1 + if (uv2 && uv2Dest) + { + uv2->Seek((vWidthUv2 * ogreVertexIndex) + uv2Element->offset, aiOrigin_SET); + uv2->Read(&uv2Dest[newIndex], sizeUv2, 1); + uv2Dest[newIndex].y = (uv2Dest[newIndex].y * -1) + 1; // Flip UV from Ogre to Assimp form + } + } + } + + // Bones and bone weights + if (parent->skeleton && boneAssignments) + { + AssimpVertexBoneWeightList weights = src->AssimpBoneWeights(dest->mNumVertices); + std::set referencedBones = src->ReferencedBonesByWeights(); + + dest->mNumBones = referencedBones.size(); + dest->mBones = new aiBone*[dest->mNumBones]; + + size_t assimpBoneIndex = 0; + for(std::set::const_iterator rbIter=referencedBones.begin(), rbEnd=referencedBones.end(); rbIter != rbEnd; ++rbIter, ++assimpBoneIndex) + { + Bone *bone = parent->skeleton->BoneById((*rbIter)); + dest->mBones[assimpBoneIndex] = bone->ConvertToAssimpBone(parent->skeleton, weights[bone->id]); + } + } + + return dest; +} + +// MeshXml + +MeshXml::MeshXml() : + sharedVertexData(0), + skeleton(0) +{ +} + +MeshXml::~MeshXml() +{ + Reset(); +} + +void MeshXml::Reset() +{ + OGRE_SAFE_DELETE(skeleton) + OGRE_SAFE_DELETE(sharedVertexData) + + for(size_t i=0, len=subMeshes.size(); iindex == index) + return subMeshes[i]; + return 0; +} + +void MeshXml::ConvertToAssimpScene(aiScene* dest) +{ + // Setup + dest->mNumMeshes = NumSubMeshes(); + dest->mMeshes = new aiMesh*[dest->mNumMeshes]; + + // Create root node + dest->mRootNode = new aiNode(); + dest->mRootNode->mNumMeshes = dest->mNumMeshes; + dest->mRootNode->mMeshes = new unsigned int[dest->mRootNode->mNumMeshes]; + + // Export meshes + for(size_t i=0; imNumMeshes; ++i) + { + dest->mMeshes[i] = subMeshes[i]->ConvertToAssimpMesh(this); + dest->mRootNode->mMeshes[i] = i; + } + + // Export skeleton + if (skeleton) + { + // Bones + if (!skeleton->bones.empty()) + { + BoneList rootBones = skeleton->RootBones(); + dest->mRootNode->mNumChildren = rootBones.size(); + dest->mRootNode->mChildren = new aiNode*[dest->mRootNode->mNumChildren]; + + for(size_t i=0, len=rootBones.size(); imRootNode->mChildren[i] = rootBones[i]->ConvertToAssimpNode(skeleton, dest->mRootNode); + } + } + + // Animations + if (!skeleton->animations.empty()) + { + dest->mNumAnimations = skeleton->animations.size(); + dest->mAnimations = new aiAnimation*[dest->mNumAnimations]; + + for(size_t i=0, len=skeleton->animations.size(); imAnimations[i] = skeleton->animations[i]->ConvertToAssimpAnimation(); + } + } + } +} + +// SubMeshXml + +SubMeshXml::SubMeshXml() : + vertexData(0), + indexData(new IndexDataXml()) +{ +} + +SubMeshXml::~SubMeshXml() +{ + Reset(); +} + +void SubMeshXml::Reset() +{ + OGRE_SAFE_DELETE(indexData) + OGRE_SAFE_DELETE(vertexData) +} + +aiMesh *SubMeshXml::ConvertToAssimpMesh(MeshXml *parent) +{ + aiMesh *dest = new aiMesh(); + dest->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + + if (!name.empty()) + dest->mName = name; + + // Material index + if (materialIndex != -1) + dest->mMaterialIndex = materialIndex; + + // Faces + dest->mNumFaces = indexData->faceCount; + dest->mFaces = new aiFace[dest->mNumFaces]; + + // Assimp required unique vertices, we need to convert from Ogres shared indexing. + size_t uniqueVertexCount = dest->mNumFaces * 3; + dest->mNumVertices = uniqueVertexCount; + dest->mVertices = new aiVector3D[dest->mNumVertices]; + + VertexDataXml *src = (!usesSharedVertexData ? vertexData : parent->sharedVertexData); + bool boneAssignments = src->HasBoneAssignments(); + bool normals = src->HasNormals(); + size_t uvs = src->NumUvs(); + + // Prepare normals + if (normals) + dest->mNormals = new aiVector3D[dest->mNumVertices]; + + // Prepare UVs + for(size_t uvi=0; uvimNumUVComponents[uvi] = 2; + dest->mTextureCoords[uvi] = new aiVector3D[dest->mNumVertices]; + } + + for (size_t fi=0; fimNumFaces; ++fi) + { + // Source Ogre face + aiFace &ogreFace = indexData->faces[fi]; + + // Destination Assimp face + aiFace &face = dest->mFaces[fi]; + face.mNumIndices = 3; + face.mIndices = new unsigned int[3]; + + const size_t pos = fi * 3; + for (size_t v=0; v<3; ++v) + { + const size_t newIndex = pos + v; + + // Write face index + face.mIndices[v] = newIndex; + + // Ogres vertex index to ref into the source buffers. + const size_t ogreVertexIndex = ogreFace.mIndices[v]; + src->AddVertexMapping(ogreVertexIndex, newIndex); + + // Position + dest->mVertices[newIndex] = src->positions[ogreVertexIndex]; + + // Normal + if (normals) + dest->mNormals[newIndex] = src->normals[ogreVertexIndex]; + + // UVs + for(size_t uvi=0; uvimTextureCoords[uvi]; + std::vector &uvSrc = src->uvs[uvi]; + uvDest[newIndex] = uvSrc[ogreVertexIndex]; + } + } + } + + // Bones and bone weights + if (parent->skeleton && boneAssignments) + { + AssimpVertexBoneWeightList weights = src->AssimpBoneWeights(dest->mNumVertices); + std::set referencedBones = src->ReferencedBonesByWeights(); + + dest->mNumBones = referencedBones.size(); + dest->mBones = new aiBone*[dest->mNumBones]; + + size_t assimpBoneIndex = 0; + for(std::set::const_iterator rbIter=referencedBones.begin(), rbEnd=referencedBones.end(); rbIter != rbEnd; ++rbIter, ++assimpBoneIndex) + { + Bone *bone = parent->skeleton->BoneById((*rbIter)); + dest->mBones[assimpBoneIndex] = bone->ConvertToAssimpBone(parent->skeleton, weights[bone->id]); + } + } + + return dest; +} + +// Animation + +Animation::Animation(Skeleton *parent) : + parentSkeleton(parent), + parentMesh(0), + length(0.0f), + baseTime(-1.0f) +{ +} + +Animation::Animation(Mesh *parent) : + parentMesh(parent), + parentSkeleton(0), + length(0.0f), + baseTime(-1.0f) +{ +} + +VertexData *Animation::AssociatedVertexData(VertexAnimationTrack *track) const +{ + if (!parentMesh) + return 0; + + bool sharedGeom = (track->target == 0); + if (sharedGeom) + return parentMesh->sharedVertexData; + else + return parentMesh->GetSubMesh(track->target-1)->vertexData; +} + +aiAnimation *Animation::ConvertToAssimpAnimation() +{ + aiAnimation *anim = new aiAnimation(); + anim->mName = name; + anim->mDuration = static_cast(length); + anim->mTicksPerSecond = 1.0; + + // Tracks + if (!tracks.empty()) + { + anim->mNumChannels = tracks.size(); + anim->mChannels = new aiNodeAnim*[anim->mNumChannels]; + + for(size_t i=0, len=tracks.size(); imChannels[i] = tracks[i].ConvertToAssimpAnimationNode(parentSkeleton); + } + } + return anim; +} + +// Skeleton + +Skeleton::Skeleton() : + blendMode(ANIMBLEND_AVERAGE) +{ +} + +Skeleton::~Skeleton() +{ + Reset(); +} + +void Skeleton::Reset() +{ + for(size_t i=0, len=bones.size(); iIsParented()) + rootBones.push_back((*iter)); + } + return rootBones; +} + +size_t Skeleton::NumRootBones() const +{ + size_t num = 0; + for(BoneList::const_iterator iter = bones.begin(); iter != bones.end(); ++iter) + { + if (!(*iter)->IsParented()) + num++; + } + return num; +} + +Bone *Skeleton::BoneByName(const std::string &name) const +{ + for(BoneList::const_iterator iter = bones.begin(); iter != bones.end(); ++iter) + { + if ((*iter)->name == name) + return (*iter); + } + return 0; +} + +Bone *Skeleton::BoneById(uint16_t id) const +{ + for(BoneList::const_iterator iter = bones.begin(); iter != bones.end(); ++iter) + { + if ((*iter)->id == id) + return (*iter); + } + return 0; +} + +// Bone + +Bone::Bone() : + id(0), + parent(0), + parentId(-1), + scale(1.0f, 1.0f, 1.0f) +{ +} + +bool Bone::IsParented() const +{ + return (parentId != -1 && parent != 0); +} + +uint16_t Bone::ParentId() const +{ + return static_cast(parentId); +} + +void Bone::AddChild(Bone *bone) +{ + if (!bone) + return; + if (bone->IsParented()) + throw DeadlyImportError("Attaching child Bone that is already parented: " + bone->name); + + bone->parent = this; + bone->parentId = id; + children.push_back(bone->id); +} + +void Bone::CalculateWorldMatrixAndDefaultPose(Skeleton *skeleton) +{ + if (!IsParented()) + worldMatrix = aiMatrix4x4(scale, rotation, position).Inverse(); + else + worldMatrix = aiMatrix4x4(scale, rotation, position).Inverse() * parent->worldMatrix; + + defaultPose = aiMatrix4x4(scale, rotation, position); + + // Recursively for all children now that the parent matrix has been calculated. + for (size_t i=0, len=children.size(); iBoneById(children[i]); + if (!child) { + throw DeadlyImportError(Formatter::format() << "CalculateWorldMatrixAndDefaultPose: Failed to find child bone " << children[i] << " for parent " << id << " " << name); + } + child->CalculateWorldMatrixAndDefaultPose(skeleton); + } +} + +aiNode *Bone::ConvertToAssimpNode(Skeleton *skeleton, aiNode *parentNode) +{ + // Bone node + aiNode* node = new aiNode(name); + node->mParent = parentNode; + node->mTransformation = defaultPose; + + // Children + if (!children.empty()) + { + node->mNumChildren = children.size(); + node->mChildren = new aiNode*[node->mNumChildren]; + + for(size_t i=0, len=children.size(); iBoneById(children[i]); + if (!child) { + throw DeadlyImportError(Formatter::format() << "ConvertToAssimpNode: Failed to find child bone " << children[i] << " for parent " << id << " " << name); + } + node->mChildren[i] = child->ConvertToAssimpNode(skeleton, node); + } + } + return node; +} + +aiBone *Bone::ConvertToAssimpBone(Skeleton *parent, const std::vector &boneWeights) +{ + aiBone *bone = new aiBone(); + bone->mName = name; + bone->mOffsetMatrix = worldMatrix; + + if (!boneWeights.empty()) + { + bone->mNumWeights = boneWeights.size(); + bone->mWeights = new aiVertexWeight[boneWeights.size()]; + memcpy(bone->mWeights, &boneWeights[0], boneWeights.size() * sizeof(aiVertexWeight)); + } + + return bone; +} + +// VertexAnimationTrack + +VertexAnimationTrack::VertexAnimationTrack() : + target(0), + type(VAT_NONE) +{ +} + +aiNodeAnim *VertexAnimationTrack::ConvertToAssimpAnimationNode(Skeleton *skeleton) +{ + if (boneName.empty() || type != VAT_TRANSFORM) { + throw DeadlyImportError("VertexAnimationTrack::ConvertToAssimpAnimationNode: Cannot convert track that has no target bone name or is not type of VAT_TRANSFORM"); + } + + aiNodeAnim *nodeAnim = new aiNodeAnim(); + nodeAnim->mNodeName = boneName; + + Bone *bone = skeleton->BoneByName(boneName); + if (!bone) { + throw DeadlyImportError("VertexAnimationTrack::ConvertToAssimpAnimationNode: Failed to find bone " + boneName + " from parent Skeleton"); + } + + // Keyframes + size_t numKeyframes = transformKeyFrames.size(); + + nodeAnim->mPositionKeys = new aiVectorKey[numKeyframes]; + nodeAnim->mRotationKeys = new aiQuatKey[numKeyframes]; + nodeAnim->mScalingKeys = new aiVectorKey[numKeyframes]; + nodeAnim->mNumPositionKeys = numKeyframes; + nodeAnim->mNumRotationKeys = numKeyframes; + nodeAnim->mNumScalingKeys = numKeyframes; + + for(size_t kfi=0; kfidefaultPose * kfSource.Transform(); + finalTransform.Decompose(scale, rot, pos); + + double t = static_cast(kfSource.timePos); + nodeAnim->mPositionKeys[kfi].mTime = t; + nodeAnim->mRotationKeys[kfi].mTime = t; + nodeAnim->mScalingKeys[kfi].mTime = t; + + nodeAnim->mPositionKeys[kfi].mValue = pos; + nodeAnim->mRotationKeys[kfi].mValue = rot; + nodeAnim->mScalingKeys[kfi].mValue = scale; + } + + return nodeAnim; +} + +// TransformKeyFrame + +TransformKeyFrame::TransformKeyFrame() : + timePos(0.0f), + scale(1.0f, 1.0f, 1.0f) +{ +} + +aiMatrix4x4 TransformKeyFrame::Transform() +{ + return aiMatrix4x4(scale, rotation, position); +} + +} // Ogre +} // Assimp + +#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER diff --git a/code/OgreStructs.h b/code/OgreStructs.h new file mode 100644 index 000000000..75cadf4b7 --- /dev/null +++ b/code/OgreStructs.h @@ -0,0 +1,681 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#ifndef AI_OGRESTRUCTS_H_INC +#define AI_OGRESTRUCTS_H_INC + +#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER + +#include "AssimpPCH.h" +#include "MemoryIOWrapper.h" + +/** @note Parts of this implementation, for example enums, deserialization constants and logic + has been copied directly with minor modifications from the MIT licensed Ogre3D code base. + See more from https://bitbucket.org/sinbad/ogre. */ + +namespace Assimp +{ +namespace Ogre +{ + +// Forward decl +class Mesh; +class MeshXml; +class SubMesh; +class SubMeshXml; +class Skeleton; + +#define OGRE_SAFE_DELETE(p) delete p; p=0; + +// Typedefs +typedef Assimp::MemoryIOStream MemoryStream; +typedef boost::shared_ptr MemoryStreamPtr; +typedef std::map VertexBufferBindings; + +// Ogre Vertex Element +class VertexElement +{ +public: + /// Vertex element semantics, used to identify the meaning of vertex buffer contents + enum Semantic { + /// Position, 3 reals per vertex + VES_POSITION = 1, + /// Blending weights + VES_BLEND_WEIGHTS = 2, + /// Blending indices + VES_BLEND_INDICES = 3, + /// Normal, 3 reals per vertex + VES_NORMAL = 4, + /// Diffuse colours + VES_DIFFUSE = 5, + /// Specular colours + VES_SPECULAR = 6, + /// Texture coordinates + VES_TEXTURE_COORDINATES = 7, + /// Binormal (Y axis if normal is Z) + VES_BINORMAL = 8, + /// Tangent (X axis if normal is Z) + VES_TANGENT = 9, + /// The number of VertexElementSemantic elements (note - the first value VES_POSITION is 1) + VES_COUNT = 9 + }; + + /// Vertex element type, used to identify the base types of the vertex contents + enum Type + { + VET_FLOAT1 = 0, + VET_FLOAT2 = 1, + VET_FLOAT3 = 2, + VET_FLOAT4 = 3, + /// alias to more specific colour type - use the current rendersystem's colour packing + VET_COLOUR = 4, + VET_SHORT1 = 5, + VET_SHORT2 = 6, + VET_SHORT3 = 7, + VET_SHORT4 = 8, + VET_UBYTE4 = 9, + /// D3D style compact colour + VET_COLOUR_ARGB = 10, + /// GL style compact colour + VET_COLOUR_ABGR = 11, + VET_DOUBLE1 = 12, + VET_DOUBLE2 = 13, + VET_DOUBLE3 = 14, + VET_DOUBLE4 = 15, + VET_USHORT1 = 16, + VET_USHORT2 = 17, + VET_USHORT3 = 18, + VET_USHORT4 = 19, + VET_INT1 = 20, + VET_INT2 = 21, + VET_INT3 = 22, + VET_INT4 = 23, + VET_UINT1 = 24, + VET_UINT2 = 25, + VET_UINT3 = 26, + VET_UINT4 = 27 + }; + + VertexElement(); + + /// Size of the vertex element in bytes. + size_t Size() const; + + /// Count of components in this element, eg. VET_FLOAT3 return 3. + size_t ComponentCount() const; + + /// Type as string. + std::string TypeToString(); + + /// Semantic as string. + std::string SemanticToString(); + + static size_t TypeSize(Type type); + static size_t ComponentCount(Type type); + static std::string TypeToString(Type type); + static std::string SemanticToString(Semantic semantic); + + uint16_t index; + uint16_t source; + uint16_t offset; + Type type; + Semantic semantic; +}; +typedef std::vector VertexElementList; + +/// Ogre Vertex Bone Assignment +struct VertexBoneAssignment +{ + uint32_t vertexIndex; + uint16_t boneIndex; + float weight; +}; +typedef std::vector VertexBoneAssignmentList; +typedef std::map VertexBoneAssignmentsMap; +typedef std::map > AssimpVertexBoneWeightList; + +// Ogre Vertex Data interface, inherited by the binary and XML implementations. +class IVertexData +{ +public: + IVertexData(); + + /// Returns if bone assignments are available. + bool HasBoneAssignments() const; + + /// Add vertex mapping from old to new index. + void AddVertexMapping(uint32_t oldIndex, uint32_t newIndex); + + /// Returns re-mapped bone assignments. + /** @note Uses mappings added via AddVertexMapping. */ + AssimpVertexBoneWeightList AssimpBoneWeights(size_t vertices); + + /// Returns a set of bone indexes that are referenced by bone assignments (weights). + std::set ReferencedBonesByWeights() const; + + /// Vertex count. + uint32_t count; + + /// Bone assignments. + VertexBoneAssignmentList boneAssignments; + +private: + void BoneAssignmentsForVertex(uint32_t currentIndex, uint32_t newIndex, VertexBoneAssignmentList &dest) const; + + std::map > vertexIndexMapping; + VertexBoneAssignmentsMap boneAssignmentsMap; +}; + +// Ogre Vertex Data +class VertexData : public IVertexData +{ +public: + VertexData(); + ~VertexData(); + + /// Releases all memory that this data structure owns. + void Reset(); + + /// Get vertex size for @c source. + uint32_t VertexSize(uint16_t source) const; + + /// Get vertex buffer for @c source. + MemoryStream *VertexBuffer(uint16_t source); + + /// Get vertex element for @c semantic for @c index. + VertexElement *GetVertexElement(VertexElement::Semantic semantic, uint16_t index = 0); + + /// Vertex elements. + VertexElementList vertexElements; + + /// Vertex buffers mapped to bind index. + VertexBufferBindings vertexBindings; +}; + +// Ogre Index Data +class IndexData +{ +public: + IndexData(); + ~IndexData(); + + /// Releases all memory that this data structure owns. + void Reset(); + + /// Index size in bytes. + size_t IndexSize() const; + + /// Face size in bytes. + size_t FaceSize() const; + + /// Index count. + uint32_t count; + + /// Face count. + uint32_t faceCount; + + /// If has 32-bit indexes. + bool is32bit; + + /// Index buffer. + MemoryStreamPtr buffer; +}; + +/// Ogre Pose +class Pose +{ +public: + struct Vertex + { + uint32_t index; + aiVector3D offset; + aiVector3D normal; + }; + typedef std::map PoseVertexMap; + + Pose() : target(0), hasNormals(false) {} + + /// Name. + std::string name; + + /// Target. + uint16_t target; + + /// Does vertices map have normals. + bool hasNormals; + + /// Vertex offset and normals. + PoseVertexMap vertices; +}; +typedef std::vector PoseList; + +/// Ogre Pose Key Frame Ref +struct PoseRef +{ + uint16_t index; + float influence; +}; +typedef std::vector PoseRefList; + +/// Ogre Pose Key Frame +struct PoseKeyFrame +{ + /// Time position in the animation. + float timePos; + + PoseRefList references; +}; +typedef std::vector PoseKeyFrameList; + +/// Ogre Morph Key Frame +struct MorphKeyFrame +{ + /// Time position in the animation. + float timePos; + + MemoryStreamPtr buffer; +}; +typedef std::vector MorphKeyFrameList; + +/// Ogre animation key frame +struct TransformKeyFrame +{ + TransformKeyFrame(); + + aiMatrix4x4 Transform(); + + float timePos; + + aiQuaternion rotation; + aiVector3D position; + aiVector3D scale; +}; +typedef std::vector TransformKeyFrameList; + +/// Ogre Animation Track +struct VertexAnimationTrack +{ + enum Type + { + /// No animation + VAT_NONE = 0, + /// Morph animation is made up of many interpolated snapshot keyframes + VAT_MORPH = 1, + /// Pose animation is made up of a single delta pose keyframe + VAT_POSE = 2, + /// Keyframe that has its on pos, rot and scale for a time position + VAT_TRANSFORM = 3 + }; + + VertexAnimationTrack(); + + /// Convert to Assimp node animation. + aiNodeAnim *ConvertToAssimpAnimationNode(Skeleton *skeleton); + + // Animation type. + Type type; + + /// Vertex data target. + /** 0 == shared geometry + >0 == submesh index + 1 */ + uint16_t target; + + /// Only valid for VAT_TRANSFORM. + std::string boneName; + + /// Only one of these will contain key frames, depending on the type enum. + PoseKeyFrameList poseKeyFrames; + MorphKeyFrameList morphKeyFrames; + TransformKeyFrameList transformKeyFrames; +}; +typedef std::vector VertexAnimationTrackList; + +/// Ogre Animation +class Animation +{ +public: + Animation(Skeleton *parent); + Animation(Mesh *parent); + + /// Returns the associated vertex data for a track in this animation. + /** @note Only valid to call when parent Mesh is set. */ + VertexData *AssociatedVertexData(VertexAnimationTrack *track) const; + + /// Convert to Assimp animation. + aiAnimation *ConvertToAssimpAnimation(); + + /// Parent mesh. + /** @note Set only when animation is read from a mesh. */ + Mesh *parentMesh; + + /// Parent skeleton. + /** @note Set only when animation is read from a skeleton. */ + Skeleton *parentSkeleton; + + /// Animation name. + std::string name; + + /// Base animation name. + std::string baseName; + + /// Length in seconds. + float length; + + /// Base animation key time. + float baseTime; + + /// Animation tracks. + VertexAnimationTrackList tracks; +}; +typedef std::vector AnimationList; + +/// Ogre Bone +class Bone +{ +public: + Bone(); + + /// Returns if this bone is parented. + bool IsParented() const; + + /// Parent index as uint16_t. Internally int32_t as -1 means unparented. + uint16_t ParentId() const; + + /// Add child bone. + void AddChild(Bone *bone); + + /// Calculates the world matrix for bone and its children. + void CalculateWorldMatrixAndDefaultPose(Skeleton *skeleton); + + /// Convert to Assimp node (animation nodes). + aiNode *ConvertToAssimpNode(Skeleton *parent, aiNode *parentNode = 0); + + /// Convert to Assimp bone (mesh bones). + aiBone *ConvertToAssimpBone(Skeleton *parent, const std::vector &boneWeights); + + uint16_t id; + std::string name; + + Bone *parent; + int32_t parentId; + std::vector children; + + aiVector3D position; + aiQuaternion rotation; + aiVector3D scale; + + aiMatrix4x4 worldMatrix; + aiMatrix4x4 defaultPose; +}; +typedef std::vector BoneList; + +/// Ogre Skeleton +class Skeleton +{ +public: + enum BlendMode + { + /// Animations are applied by calculating a weighted average of all animations + ANIMBLEND_AVERAGE = 0, + /// Animations are applied by calculating a weighted cumulative total + ANIMBLEND_CUMULATIVE = 1 + }; + + Skeleton(); + ~Skeleton(); + + /// Releases all memory that this data structure owns. + void Reset(); + + /// Returns unparented root bones. + BoneList RootBones() const; + + /// Returns number of unparented root bones. + size_t NumRootBones() const; + + /// Get bone by name. + Bone *BoneByName(const std::string &name) const; + + /// Get bone by id. + Bone *BoneById(uint16_t id) const; + + BoneList bones; + AnimationList animations; + + /// @todo Take blend mode into account, but where? + BlendMode blendMode; +}; + +/// Ogre Sub Mesh interface, inherited by the binary and XML implementations. +class ISubMesh +{ +public: + /// @note Full list of Ogre types, not all of them are supported and exposed to Assimp. + enum OperationType + { + /// A list of points, 1 vertex per point + OT_POINT_LIST = 1, + /// A list of lines, 2 vertices per line + OT_LINE_LIST = 2, + /// A strip of connected lines, 1 vertex per line plus 1 start vertex + OT_LINE_STRIP = 3, + /// A list of triangles, 3 vertices per triangle + OT_TRIANGLE_LIST = 4, + /// A strip of triangles, 3 vertices for the first triangle, and 1 per triangle after that + OT_TRIANGLE_STRIP = 5, + /// A fan of triangles, 3 vertices for the first triangle, and 1 per triangle after that + OT_TRIANGLE_FAN = 6 + }; + + ISubMesh(); + + /// SubMesh index. + unsigned int index; + + /// SubMesh name. + std::string name; + + /// Material used by this submesh. + std::string materialRef; + + /// Texture alias information. + std::string textureAliasName; + std::string textureAliasRef; + + /// Assimp scene material index used by this submesh. + /** -1 if no material or material could not be imported. */ + int materialIndex; + + /// If submesh uses shared geometry from parent mesh. + bool usesSharedVertexData; + + /// Operation type. + OperationType operationType; +}; + +/// Ogre SubMesh +class SubMesh : public ISubMesh +{ +public: + SubMesh(); + ~SubMesh(); + + /// Releases all memory that this data structure owns. + /** @note Vertex and index data contains shared ptrs + that are freed automatically. In practice the ref count + should be 0 after this reset. */ + void Reset(); + + /// Covert to Assimp mesh. + aiMesh *ConvertToAssimpMesh(Mesh *parent); + + /// Vertex data. + VertexData *vertexData; + + /// Index data. + IndexData *indexData; +}; +typedef std::vector SubMeshList; + +/// Ogre Mesh +class Mesh +{ +public: + Mesh(); + ~Mesh(); + + /// Releases all memory that this data structure owns. + void Reset(); + + /// Returns number of subMeshes. + size_t NumSubMeshes() const; + + /// Returns submesh for @c index. + SubMesh *GetSubMesh(uint16_t index) const; + + /// Convert mesh to Assimp scene. + void ConvertToAssimpScene(aiScene* dest); + + /// Mesh has skeletal animations. + bool hasSkeletalAnimations; + + /// Skeleton reference. + std::string skeletonRef; + + /// Skeleton. + Skeleton *skeleton; + + /// Vertex data + VertexData *sharedVertexData; + + /// Sub meshes. + SubMeshList subMeshes; + + /// Animations + AnimationList animations; + + /// Poses + PoseList poses; +}; + +/// Ogre XML Vertex Data +class VertexDataXml : public IVertexData +{ +public: + VertexDataXml(); + + bool HasPositions() const; + bool HasNormals() const; + bool HasTangents() const; + bool HasUvs() const; + size_t NumUvs() const; + + std::vector positions; + std::vector normals; + std::vector tangents; + std::vector > uvs; +}; + +/// Ogre XML Index Data +class IndexDataXml +{ +public: + IndexDataXml() : faceCount(0) {} + + /// Face count. + uint32_t faceCount; + + std::vector faces; +}; + +/// Ogre XML SubMesh +class SubMeshXml : public ISubMesh +{ +public: + SubMeshXml(); + ~SubMeshXml(); + + /// Releases all memory that this data structure owns. + void Reset(); + + aiMesh *ConvertToAssimpMesh(MeshXml *parent); + + IndexDataXml *indexData; + VertexDataXml *vertexData; +}; +typedef std::vector SubMeshXmlList; + +/// Ogre XML Mesh +class MeshXml +{ +public: + MeshXml(); + ~MeshXml(); + + /// Releases all memory that this data structure owns. + void Reset(); + + /// Returns number of subMeshes. + size_t NumSubMeshes() const; + + /// Returns submesh for @c index. + SubMeshXml *GetSubMesh(uint16_t index) const; + + /// Convert mesh to Assimp scene. + void ConvertToAssimpScene(aiScene* dest); + + /// Skeleton reference. + std::string skeletonRef; + + /// Skeleton. + Skeleton *skeleton; + + /// Vertex data + VertexDataXml *sharedVertexData; + + /// Sub meshes. + SubMeshXmlList subMeshes; +}; + +} // Ogre +} // Assimp + +#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER +#endif // AI_OGRESTRUCTS_H_INC diff --git a/code/OgreXmlSerializer.cpp b/code/OgreXmlSerializer.cpp new file mode 100644 index 000000000..733e36c03 --- /dev/null +++ b/code/OgreXmlSerializer.cpp @@ -0,0 +1,1003 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#include "OgreXmlSerializer.h" +#include "OgreBinarySerializer.h" +#include "OgreParsingUtils.h" + +#include "TinyFormatter.h" + +#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER + +// Define as 1 to get verbose logging. +#define OGRE_XML_SERIALIZER_DEBUG 0 + +namespace Assimp +{ +namespace Ogre +{ + +void ThrowAttibuteError(const XmlReader* reader, const std::string &name, const std::string &error = "") +{ + if (!error.empty()) + { + throw DeadlyImportError(error + " in node '" + std::string(reader->getNodeName()) + "' and attribute '" + name + "'"); + } + else + { + throw DeadlyImportError("Attribute '" + name + "' does not exist in node '" + std::string(reader->getNodeName()) + "'"); + } +} + +template<> +int32_t OgreXmlSerializer::ReadAttribute(const std::string &name) const +{ + if (HasAttribute(name.c_str())) + { + return static_cast(m_reader->getAttributeValueAsInt(name.c_str())); + } + else + { + ThrowAttibuteError(m_reader, name); + return 0; + } +} + +template<> +uint32_t OgreXmlSerializer::ReadAttribute(const std::string &name) const +{ + if (HasAttribute(name.c_str())) + { + /** @note This is hackish. But we are never expecting unsigned values that go outside the + int32_t range. Just monitor for negative numbers and kill the import. */ + int32_t temp = ReadAttribute(name); + if (temp >= 0) + { + return static_cast(temp); + } + else + { + ThrowAttibuteError(m_reader, name, "Found a negative number value where expecting a uint32_t value"); + } + } + else + { + ThrowAttibuteError(m_reader, name); + } + return 0; +} + +template<> +uint16_t OgreXmlSerializer::ReadAttribute(const std::string &name) const +{ + if (HasAttribute(name.c_str())) + { + return static_cast(ReadAttribute(name)); + } + else + { + ThrowAttibuteError(m_reader, name); + } + return 0; +} + +template<> +float OgreXmlSerializer::ReadAttribute(const std::string &name) const +{ + if (HasAttribute(name.c_str())) + { + return m_reader->getAttributeValueAsFloat(name.c_str()); + } + else + { + ThrowAttibuteError(m_reader, name); + return 0; + } +} + +template<> +std::string OgreXmlSerializer::ReadAttribute(const std::string &name) const +{ + const char* value = m_reader->getAttributeValue(name.c_str()); + if (value) + { + return std::string(value); + } + else + { + ThrowAttibuteError(m_reader, name); + return ""; + } +} + +template<> +bool OgreXmlSerializer::ReadAttribute(const std::string &name) const +{ + std::string value = Ogre::ToLower(ReadAttribute(name)); + if (ASSIMP_stricmp(value, "true") == 0) + { + return true; + } + else if (ASSIMP_stricmp(value, "false") == 0) + { + return false; + } + else + { + ThrowAttibuteError(m_reader, name, "Boolean value is expected to be 'true' or 'false', encountered '" + value + "'"); + return false; + } +} + +bool OgreXmlSerializer::HasAttribute(const std::string &name) const +{ + return (m_reader->getAttributeValue(name.c_str()) != 0); +} + +std::string &OgreXmlSerializer::NextNode() +{ + do + { + if (!m_reader->read()) + { + m_currentNodeName = ""; + return m_currentNodeName; + } + } + while(m_reader->getNodeType() != irr::io::EXN_ELEMENT); + + CurrentNodeName(true); +#if (OGRE_XML_SERIALIZER_DEBUG == 1) + DefaultLogger::get()->debug("<" + m_currentNodeName + ">"); +#endif + return m_currentNodeName; +} + +bool OgreXmlSerializer::CurrentNodeNameEquals(const std::string &name) const +{ + return (ASSIMP_stricmp(m_currentNodeName, name) == 0); +} + +std::string OgreXmlSerializer::CurrentNodeName(bool forceRead) +{ + if (forceRead) + m_currentNodeName = std::string(m_reader->getNodeName()); + return m_currentNodeName; +} + +std::string &OgreXmlSerializer::SkipCurrentNode() +{ +#if (OGRE_XML_SERIALIZER_DEBUG == 1) + DefaultLogger::get()->debug("Skipping node <" + m_currentNodeName + ">"); +#endif + + for(;;) + { + if (!m_reader->read()) + { + m_currentNodeName = ""; + return m_currentNodeName; + } + if (m_reader->getNodeType() != irr::io::EXN_ELEMENT_END) + continue; + else if (std::string(m_reader->getNodeName()) == m_currentNodeName) + break; + } + return NextNode(); +} + +// Mesh XML constants + +// +const std::string nnMesh = "mesh"; +const std::string nnSharedGeometry = "sharedgeometry"; +const std::string nnSubMeshes = "submeshes"; +const std::string nnSubMesh = "submesh"; +const std::string nnSubMeshNames = "submeshnames"; +const std::string nnSkeletonLink = "skeletonlink"; +const std::string nnLOD = "levelofdetail"; +const std::string nnExtremes = "extremes"; +const std::string nnPoses = "poses"; +const std::string nnAnimations = "animations"; + +// +const std::string nnFaces = "faces"; +const std::string nnFace = "face"; +const std::string nnGeometry = "geometry"; +const std::string nnTextures = "textures"; + +// +const std::string nnBoneAssignments = "boneassignments"; + +// +const std::string nnVertexBuffer = "vertexbuffer"; + +// +const std::string nnVertex = "vertex"; +const std::string nnPosition = "position"; +const std::string nnNormal = "normal"; +const std::string nnTangent = "tangent"; +const std::string nnBinormal = "binormal"; +const std::string nnTexCoord = "texcoord"; +const std::string nnColorDiffuse = "colour_diffuse"; +const std::string nnColorSpecular = "colour_specular"; + +// +const std::string nnVertexBoneAssignment = "vertexboneassignment"; + +// Skeleton XML constants + +// +const std::string nnSkeleton = "skeleton"; +const std::string nnBones = "bones"; +const std::string nnBoneHierarchy = "bonehierarchy"; +const std::string nnAnimationLinks = "animationlinks"; + +// +const std::string nnBone = "bone"; +const std::string nnRotation = "rotation"; +const std::string nnAxis = "axis"; +const std::string nnScale = "scale"; + +// +const std::string nnBoneParent = "boneparent"; + +// +const std::string nnAnimation = "animation"; +const std::string nnTracks = "tracks"; + +// +const std::string nnTrack = "track"; +const std::string nnKeyFrames = "keyframes"; +const std::string nnKeyFrame = "keyframe"; +const std::string nnTranslate = "translate"; +const std::string nnRotate = "rotate"; + +// Common XML constants + +const std::string anX = "x"; +const std::string anY = "y"; +const std::string anZ = "z"; + +// Mesh + +MeshXml *OgreXmlSerializer::ImportMesh(XmlReader *reader) +{ + OgreXmlSerializer serializer(reader); + + MeshXml *mesh = new MeshXml(); + serializer.ReadMesh(mesh); + return mesh; +} + +void OgreXmlSerializer::ReadMesh(MeshXml *mesh) +{ + if (NextNode() != nnMesh) { + throw DeadlyImportError("Root node is <" + m_currentNodeName + "> expecting "); + } + + DefaultLogger::get()->debug("Reading Mesh"); + + NextNode(); + + // Root level nodes + while(m_currentNodeName == nnSharedGeometry || + m_currentNodeName == nnSubMeshes || + m_currentNodeName == nnSkeletonLink || + m_currentNodeName == nnBoneAssignments || + m_currentNodeName == nnLOD || + m_currentNodeName == nnSubMeshNames || + m_currentNodeName == nnExtremes || + m_currentNodeName == nnPoses || + m_currentNodeName == nnAnimations) + { + if (m_currentNodeName == nnSharedGeometry) + { + mesh->sharedVertexData = new VertexDataXml(); + ReadGeometry(mesh->sharedVertexData); + } + else if (m_currentNodeName == nnSubMeshes) + { + NextNode(); + while(m_currentNodeName == nnSubMesh) { + ReadSubMesh(mesh); + } + } + else if (m_currentNodeName == nnBoneAssignments) + { + ReadBoneAssignments(mesh->sharedVertexData); + } + else if (m_currentNodeName == nnSkeletonLink) + { + mesh->skeletonRef = ReadAttribute("name"); + DefaultLogger::get()->debug("Read skeleton link " + mesh->skeletonRef); + NextNode(); + } + // Assimp incompatible/ignored nodes + else + SkipCurrentNode(); + } +} + +void OgreXmlSerializer::ReadGeometry(VertexDataXml *dest) +{ + dest->count = ReadAttribute("vertexcount"); + DefaultLogger::get()->debug(Formatter::format() << " - Reading geometry of " << dest->count << " vertices"); + + NextNode(); + while(m_currentNodeName == nnVertexBuffer) { + ReadGeometryVertexBuffer(dest); + } +} + +void OgreXmlSerializer::ReadGeometryVertexBuffer(VertexDataXml *dest) +{ + bool positions = (HasAttribute("positions") && ReadAttribute("positions")); + bool normals = (HasAttribute("normals") && ReadAttribute("normals")); + bool tangents = (HasAttribute("tangents") && ReadAttribute("tangents")); + uint32_t uvs = (HasAttribute("texture_coords") ? ReadAttribute("texture_coords") : 0); + + // Not having positions is a error only if a previous vertex buffer did not have them. + if (!positions && !dest->HasPositions()) { + throw DeadlyImportError("Vertex buffer does not contain positions!"); + } + + if (positions) + { + DefaultLogger::get()->debug(" - Contains positions"); + dest->positions.reserve(dest->count); + } + if (normals) + { + DefaultLogger::get()->debug(" - Contains normals"); + dest->normals.reserve(dest->count); + } + if (tangents) + { + DefaultLogger::get()->debug(" - Contains tangents"); + dest->tangents.reserve(dest->count); + } + if (uvs > 0) + { + DefaultLogger::get()->debug(Formatter::format() << " - Contains " << uvs << " texture coords"); + dest->uvs.resize(uvs); + for(size_t i=0, len=dest->uvs.size(); iuvs[i].reserve(dest->count); + } + } + + bool warnBinormal = true; + bool warnColorDiffuse = true; + bool warnColorSpecular = true; + + NextNode(); + + while(m_currentNodeName == nnVertex || + m_currentNodeName == nnPosition || + m_currentNodeName == nnNormal || + m_currentNodeName == nnTangent || + m_currentNodeName == nnBinormal || + m_currentNodeName == nnTexCoord || + m_currentNodeName == nnColorDiffuse || + m_currentNodeName == nnColorSpecular) + { + if (m_currentNodeName == nnVertex) { + NextNode(); + } + + /// @todo Implement nnBinormal, nnColorDiffuse and nnColorSpecular + + if (positions && m_currentNodeName == nnPosition) + { + aiVector3D pos; + pos.x = ReadAttribute(anX); + pos.y = ReadAttribute(anY); + pos.z = ReadAttribute(anZ); + dest->positions.push_back(pos); + } + else if (normals && m_currentNodeName == nnNormal) + { + aiVector3D normal; + normal.x = ReadAttribute(anX); + normal.y = ReadAttribute(anY); + normal.z = ReadAttribute(anZ); + dest->normals.push_back(normal); + } + else if (tangents && m_currentNodeName == nnTangent) + { + aiVector3D tangent; + tangent.x = ReadAttribute(anX); + tangent.y = ReadAttribute(anY); + tangent.z = ReadAttribute(anZ); + dest->tangents.push_back(tangent); + } + else if (uvs > 0 && m_currentNodeName == nnTexCoord) + { + for(size_t i=0, len=dest->uvs.size(); i("u"); + uv.y = (ReadAttribute("v") * -1) + 1; // Flip UV from Ogre to Assimp form + dest->uvs[i].push_back(uv); + + NextNode(); + } + // Continue main loop as above already read next node + continue; + } + else + { + /// @todo Remove this stuff once implemented. We only want to log warnings once per element. + bool warn = true; + if (m_currentNodeName == nnBinormal) + { + if (warnBinormal) + { + warnBinormal = false; + } + else + { + warn = false; + } + } + else if (m_currentNodeName == nnColorDiffuse) + { + if (warnColorDiffuse) + { + warnColorDiffuse = false; + } + else + { + warn = false; + } + } + else if (m_currentNodeName == nnColorSpecular) + { + if (warnColorSpecular) + { + warnColorSpecular = false; + } + else + { + warn = false; + } + } + if (warn) { + DefaultLogger::get()->warn("Vertex buffer attribute read not implemented for element: " + m_currentNodeName); + } + } + + // Advance + NextNode(); + } + + // Sanity checks + if (dest->positions.size() != dest->count) { + throw DeadlyImportError(Formatter::format() << "Read only " << dest->positions.size() << " positions when should have read " << dest->count); + } + if (normals && dest->normals.size() != dest->count) { + throw DeadlyImportError(Formatter::format() << "Read only " << dest->normals.size() << " normals when should have read " << dest->count); + } + if (tangents && dest->tangents.size() != dest->count) { + throw DeadlyImportError(Formatter::format() << "Read only " << dest->tangents.size() << " tangents when should have read " << dest->count); + } + for(unsigned int i=0; iuvs.size(); ++i) + { + if (dest->uvs[i].size() != dest->count) { + throw DeadlyImportError(Formatter::format() << "Read only " << dest->uvs[i].size() + << " uvs for uv index " << i << " when should have read " << dest->count); + } + } +} + +void OgreXmlSerializer::ReadSubMesh(MeshXml *mesh) +{ + static const std::string anMaterial = "material"; + static const std::string anUseSharedVertices = "usesharedvertices"; + static const std::string anCount = "count"; + static const std::string anV1 = "v1"; + static const std::string anV2 = "v2"; + static const std::string anV3 = "v3"; + static const std::string anV4 = "v4"; + + SubMeshXml* submesh = new SubMeshXml(); + + if (HasAttribute(anMaterial)) { + submesh->materialRef = ReadAttribute(anMaterial); + } + if (HasAttribute(anUseSharedVertices)) { + submesh->usesSharedVertexData = ReadAttribute(anUseSharedVertices); + } + + DefaultLogger::get()->debug(Formatter::format() << "Reading SubMesh " << mesh->subMeshes.size()); + DefaultLogger::get()->debug(Formatter::format() << " - Material: '" << submesh->materialRef << "'"); + DefaultLogger::get()->debug(Formatter::format() << " - Uses shared geometry: " << (submesh->usesSharedVertexData ? "true" : "false")); + + // TODO: maybe we have always just 1 faces and 1 geometry and always in this order. this loop will only work correct, when the order + // of faces and geometry changed, and not if we have more than one of one + /// @todo Fix above comment with better read logic below + + bool quadWarned = false; + + NextNode(); + while(m_currentNodeName == nnFaces || + m_currentNodeName == nnGeometry || + m_currentNodeName == nnTextures || + m_currentNodeName == nnBoneAssignments) + { + if (m_currentNodeName == nnFaces) + { + submesh->indexData->faceCount = ReadAttribute(anCount); + submesh->indexData->faces.reserve(submesh->indexData->faceCount); + + NextNode(); + while(m_currentNodeName == nnFace) + { + aiFace face; + face.mNumIndices = 3; + face.mIndices = new unsigned int[3]; + face.mIndices[0] = ReadAttribute(anV1); + face.mIndices[1] = ReadAttribute(anV2); + face.mIndices[2] = ReadAttribute(anV3); + + /// @todo Support quads if Ogre even supports them in XML (I'm not sure but I doubt it) + if (!quadWarned && HasAttribute(anV4)) { + DefaultLogger::get()->warn("Submesh has quads with , only triangles are supported at the moment!"); + quadWarned = true; + } + + submesh->indexData->faces.push_back(face); + + // Advance + NextNode(); + } + + if (submesh->indexData->faces.size() == submesh->indexData->faceCount) + { + DefaultLogger::get()->debug(Formatter::format() << " - Faces " << submesh->indexData->faceCount); + } + else + { + throw DeadlyImportError(Formatter::format() << "Read only " << submesh->indexData->faces.size() << " faces when should have read " << submesh->indexData->faceCount); + } + } + else if (m_currentNodeName == nnGeometry) + { + if (submesh->usesSharedVertexData) { + throw DeadlyImportError("Found in when use shared geometry is true. Invalid mesh file."); + } + + submesh->vertexData = new VertexDataXml(); + ReadGeometry(submesh->vertexData); + } + else if (m_currentNodeName == nnBoneAssignments) + { + ReadBoneAssignments(submesh->vertexData); + } + // Assimp incompatible/ignored nodes + else + SkipCurrentNode(); + } + + submesh->index = mesh->subMeshes.size(); + mesh->subMeshes.push_back(submesh); +} + +void OgreXmlSerializer::ReadBoneAssignments(VertexDataXml *dest) +{ + if (!dest) { + throw DeadlyImportError("Cannot read bone assignments, vertex data is null."); + } + + static const std::string anVertexIndex = "vertexindex"; + static const std::string anBoneIndex = "boneindex"; + static const std::string anWeight = "weight"; + + std::set influencedVertices; + + NextNode(); + while(m_currentNodeName == nnVertexBoneAssignment) + { + VertexBoneAssignment ba; + ba.vertexIndex = ReadAttribute(anVertexIndex); + ba.boneIndex = ReadAttribute(anBoneIndex); + ba.weight = ReadAttribute(anWeight); + + dest->boneAssignments.push_back(ba); + influencedVertices.insert(ba.vertexIndex); + + NextNode(); + } + + /** Normalize bone weights. + Some exporters wont care if the sum of all bone weights + for a single vertex equals 1 or not, so validate here. */ + const float epsilon = 0.05f; + for(std::set::const_iterator iter=influencedVertices.begin(), end=influencedVertices.end(); iter != end; ++iter) + { + const uint32_t vertexIndex = (*iter); + + float sum = 0.0f; + for (VertexBoneAssignmentList::const_iterator baIter=dest->boneAssignments.begin(), baEnd=dest->boneAssignments.end(); baIter != baEnd; ++baIter) + { + if (baIter->vertexIndex == vertexIndex) + sum += baIter->weight; + } + if ((sum < (1.0f - epsilon)) || (sum > (1.0f + epsilon))) + { + for (VertexBoneAssignmentList::iterator baIter=dest->boneAssignments.begin(), baEnd=dest->boneAssignments.end(); baIter != baEnd; ++baIter) + { + if (baIter->vertexIndex == vertexIndex) + baIter->weight /= sum; + } + } + } + + DefaultLogger::get()->debug(Formatter::format() << " - " << dest->boneAssignments.size() << " bone assignments"); +} + +// Skeleton + +bool OgreXmlSerializer::ImportSkeleton(Assimp::IOSystem *pIOHandler, MeshXml *mesh) +{ + if (!mesh || mesh->skeletonRef.empty()) + return false; + + // Highly unusual to see in read world cases but support + // XML mesh referencing a binary skeleton file. + if (EndsWith(mesh->skeletonRef, ".skeleton", false)) + { + if (OgreBinarySerializer::ImportSkeleton(pIOHandler, mesh)) + return true; + + /** Last fallback if .skeleton failed to be read. Try reading from + .skeleton.xml even if the XML file referenced a binary skeleton. + @note This logic was in the previous version and I don't want to break + old code that might depends on it. */ + mesh->skeletonRef = mesh->skeletonRef + ".xml"; + } + + XmlReaderPtr reader = OpenReader(pIOHandler, mesh->skeletonRef); + if (!reader.get()) + return false; + + Skeleton *skeleton = new Skeleton(); + OgreXmlSerializer serializer(reader.get()); + serializer.ReadSkeleton(skeleton); + mesh->skeleton = skeleton; + return true; +} + +bool OgreXmlSerializer::ImportSkeleton(Assimp::IOSystem *pIOHandler, Mesh *mesh) +{ + if (!mesh || mesh->skeletonRef.empty()) + return false; + + XmlReaderPtr reader = OpenReader(pIOHandler, mesh->skeletonRef); + if (!reader.get()) + return false; + + Skeleton *skeleton = new Skeleton(); + OgreXmlSerializer serializer(reader.get()); + serializer.ReadSkeleton(skeleton); + mesh->skeleton = skeleton; + return true; +} + +XmlReaderPtr OgreXmlSerializer::OpenReader(Assimp::IOSystem *pIOHandler, const std::string &filename) +{ + if (!EndsWith(filename, ".skeleton.xml", false)) + { + DefaultLogger::get()->error("Imported Mesh is referencing to unsupported '" + filename + "' skeleton file."); + return XmlReaderPtr(); + } + + if (!pIOHandler->Exists(filename)) + { + DefaultLogger::get()->error("Failed to find skeleton file '" + filename + "' that is referenced by imported Mesh."); + return XmlReaderPtr(); + } + + boost::scoped_ptr file(pIOHandler->Open(filename)); + if (!file.get()) { + throw DeadlyImportError("Failed to open skeleton file " + filename); + } + + boost::scoped_ptr stream(new CIrrXML_IOStreamReader(file.get())); + XmlReaderPtr reader = XmlReaderPtr(irr::io::createIrrXMLReader(stream.get())); + if (!reader.get()) { + throw DeadlyImportError("Failed to create XML reader for skeleton file " + filename); + } + return reader; +} + +void OgreXmlSerializer::ReadSkeleton(Skeleton *skeleton) +{ + if (NextNode() != nnSkeleton) { + throw DeadlyImportError("Root node is <" + m_currentNodeName + "> expecting "); + } + + DefaultLogger::get()->debug("Reading Skeleton"); + + // Optional blend mode from root node + if (HasAttribute("blendmode")) { + skeleton->blendMode = (ToLower(ReadAttribute("blendmode")) == "cumulative" + ? Skeleton::ANIMBLEND_CUMULATIVE : Skeleton::ANIMBLEND_AVERAGE); + } + + NextNode(); + + // Root level nodes + while(m_currentNodeName == nnBones || + m_currentNodeName == nnBoneHierarchy || + m_currentNodeName == nnAnimations || + m_currentNodeName == nnAnimationLinks) + { + if (m_currentNodeName == nnBones) + ReadBones(skeleton); + else if (m_currentNodeName == nnBoneHierarchy) + ReadBoneHierarchy(skeleton); + else if (m_currentNodeName == nnAnimations) + ReadAnimations(skeleton); + else + SkipCurrentNode(); + } +} + +void OgreXmlSerializer::ReadAnimations(Skeleton *skeleton) +{ + if (skeleton->bones.empty()) { + throw DeadlyImportError("Cannot read for a Skeleton without bones"); + } + + DefaultLogger::get()->debug(" - Animations"); + + NextNode(); + while(m_currentNodeName == nnAnimation) + { + Animation *anim = new Animation(skeleton); + anim->name = ReadAttribute("name"); + anim->length = ReadAttribute("length"); + + if (NextNode() != nnTracks) { + throw DeadlyImportError(Formatter::format() << "No found in " << anim->name); + } + + ReadAnimationTracks(anim); + skeleton->animations.push_back(anim); + + DefaultLogger::get()->debug(Formatter::format() << " " << anim->name << " (" << anim->length << " sec, " << anim->tracks.size() << " tracks)"); + } +} + +void OgreXmlSerializer::ReadAnimationTracks(Animation *dest) +{ + NextNode(); + while(m_currentNodeName == nnTrack) + { + VertexAnimationTrack track; + track.type = VertexAnimationTrack::VAT_TRANSFORM; + track.boneName = ReadAttribute("bone"); + + if (NextNode() != nnKeyFrames) { + throw DeadlyImportError(Formatter::format() << "No found in " << dest->name); + } + + ReadAnimationKeyFrames(dest, &track); + + dest->tracks.push_back(track); + } +} + +void OgreXmlSerializer::ReadAnimationKeyFrames(Animation *anim, VertexAnimationTrack *dest) +{ + const aiVector3D zeroVec(0.f, 0.f, 0.f); + + NextNode(); + while(m_currentNodeName == nnKeyFrame) + { + TransformKeyFrame keyframe; + keyframe.timePos = ReadAttribute("time"); + + NextNode(); + while(m_currentNodeName == nnTranslate || m_currentNodeName == nnRotate || m_currentNodeName == nnScale) + { + if (m_currentNodeName == nnTranslate) + { + keyframe.position.x = ReadAttribute(anX); + keyframe.position.y = ReadAttribute(anY); + keyframe.position.z = ReadAttribute(anZ); + } + else if (m_currentNodeName == nnRotate) + { + float angle = ReadAttribute("angle"); + + if (NextNode() != nnAxis) { + throw DeadlyImportError("No axis specified for keyframe rotation in animation " + anim->name); + } + + aiVector3D axis; + axis.x = ReadAttribute(anX); + axis.y = ReadAttribute(anY); + axis.z = ReadAttribute(anZ); + if (axis.Equal(zeroVec)) + { + axis.x = 1.0f; + if (angle != 0) { + DefaultLogger::get()->warn("Found invalid a key frame with a zero rotation axis in animation: " + anim->name); + } + } + keyframe.rotation = aiQuaternion(axis, angle); + } + else if (m_currentNodeName == nnScale) + { + keyframe.scale.x = ReadAttribute(anX); + keyframe.scale.y = ReadAttribute(anY); + keyframe.scale.z = ReadAttribute(anZ); + } + + NextNode(); + } + + dest->transformKeyFrames.push_back(keyframe); + } +} + +void OgreXmlSerializer::ReadBoneHierarchy(Skeleton *skeleton) +{ + if (skeleton->bones.empty()) { + throw DeadlyImportError("Cannot read for a Skeleton without bones"); + } + + while(NextNode() == nnBoneParent) + { + const std::string name = ReadAttribute("bone"); + const std::string parentName = ReadAttribute("parent"); + + Bone *bone = skeleton->BoneByName(name); + Bone *parent = skeleton->BoneByName(parentName); + + if (bone && parent) + parent->AddChild(bone); + else + throw DeadlyImportError("Failed to find bones for parenting: Child " + name + " for parent " + parentName); + } + + // Calculate bone matrices for root bones. Recursively calculates their children. + for (size_t i=0, len=skeleton->bones.size(); ibones[i]; + if (!bone->IsParented()) + bone->CalculateWorldMatrixAndDefaultPose(skeleton); + } +} + +bool BoneCompare(Bone *a, Bone *b) +{ + return (a->id < b->id); +} + +void OgreXmlSerializer::ReadBones(Skeleton *skeleton) +{ + DefaultLogger::get()->debug(" - Bones"); + + NextNode(); + while(m_currentNodeName == nnBone) + { + Bone *bone = new Bone(); + bone->id = ReadAttribute("id"); + bone->name = ReadAttribute("name"); + + NextNode(); + while(m_currentNodeName == nnPosition || + m_currentNodeName == nnRotation || + m_currentNodeName == nnScale) + { + if (m_currentNodeName == nnPosition) + { + bone->position.x = ReadAttribute(anX); + bone->position.y = ReadAttribute(anY); + bone->position.z = ReadAttribute(anZ); + } + else if (m_currentNodeName == nnRotation) + { + float angle = ReadAttribute("angle"); + + if (NextNode() != nnAxis) { + throw DeadlyImportError(Formatter::format() << "No axis specified for bone rotation in bone " << bone->id); + } + + aiVector3D axis; + axis.x = ReadAttribute(anX); + axis.y = ReadAttribute(anY); + axis.z = ReadAttribute(anZ); + + bone->rotation = aiQuaternion(axis, angle); + } + else if (m_currentNodeName == nnScale) + { + /// @todo Implement taking scale into account in matrix/pose calculations! + if (HasAttribute("factor")) + { + float factor = ReadAttribute("factor"); + bone->scale.Set(factor, factor, factor); + } + else + { + if (HasAttribute(anX)) + bone->scale.x = ReadAttribute(anX); + if (HasAttribute(anY)) + bone->scale.y = ReadAttribute(anY); + if (HasAttribute(anZ)) + bone->scale.z = ReadAttribute(anZ); + } + } + + NextNode(); + } + + skeleton->bones.push_back(bone); + } + + // Order bones by Id + std::sort(skeleton->bones.begin(), skeleton->bones.end(), BoneCompare); + + // Validate that bone indexes are not skipped. + /** @note Left this from original authors code, but not sure if this is strictly necessary + as per the Ogre skeleton spec. It might be more that other (later) code in this imported does not break. */ + for (size_t i=0, len=skeleton->bones.size(); ibones[i]; + DefaultLogger::get()->debug(Formatter::format() << " " << b->id << " " << b->name); + + if (b->id != static_cast(i)) { + throw DeadlyImportError(Formatter::format() << "Bone ids are not in sequence starting from 0. Missing index " << i); + } + } +} + +} // Ogre +} // Assimp + +#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER diff --git a/code/OgreXmlSerializer.h b/code/OgreXmlSerializer.h new file mode 100644 index 000000000..62257f81c --- /dev/null +++ b/code/OgreXmlSerializer.h @@ -0,0 +1,116 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#ifndef AI_OGREXMLSERIALIZER_H_INC +#define AI_OGREXMLSERIALIZER_H_INC + +#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER + +#include "OgreStructs.h" +#include "irrXMLWrapper.h" + +namespace Assimp +{ +namespace Ogre +{ + +typedef irr::io::IrrXMLReader XmlReader; +typedef boost::shared_ptr XmlReaderPtr; + +class OgreXmlSerializer +{ +public: + /// Imports mesh and returns the result. + /** @note Fatal unrecoverable errors will throw a DeadlyImportError. */ + static MeshXml *ImportMesh(XmlReader *reader); + + /// Imports skeleton to @c mesh. + /** If mesh does not have a skeleton reference or the skeleton file + cannot be found it is not a fatal DeadlyImportError. + @return If skeleton import was successful. */ + static bool ImportSkeleton(Assimp::IOSystem *pIOHandler, MeshXml *mesh); + static bool ImportSkeleton(Assimp::IOSystem *pIOHandler, Mesh *mesh); + +private: + OgreXmlSerializer(XmlReader *reader) : + m_reader(reader) + { + } + + static XmlReaderPtr OpenReader(Assimp::IOSystem *pIOHandler, const std::string &filename); + + // Mesh + void ReadMesh(MeshXml *mesh); + void ReadSubMesh(MeshXml *mesh); + + void ReadGeometry(VertexDataXml *dest); + void ReadGeometryVertexBuffer(VertexDataXml *dest); + + void ReadBoneAssignments(VertexDataXml *dest); + + // Skeleton + void ReadSkeleton(Skeleton *skeleton); + + void ReadBones(Skeleton *skeleton); + void ReadBoneHierarchy(Skeleton *skeleton); + + void ReadAnimations(Skeleton *skeleton); + void ReadAnimationTracks(Animation *dest); + void ReadAnimationKeyFrames(Animation *anim, VertexAnimationTrack *dest); + + template + T ReadAttribute(const std::string &name) const; + bool HasAttribute(const std::string &name) const; + + std::string &NextNode(); + std::string &SkipCurrentNode(); + + bool CurrentNodeNameEquals(const std::string &name) const; + std::string CurrentNodeName(bool forceRead = false); + + XmlReader *m_reader; + std::string m_currentNodeName; +}; + +} // Ogre +} // Assimp + +#endif // ASSIMP_BUILD_NO_OGRE_IMPORTER +#endif // AI_OGREXMLSERIALIZER_H_INC diff --git a/code/OptimizeMeshes.cpp b/code/OptimizeMeshes.cpp index 937a678a6..33f9b3bfe 100644 --- a/code/OptimizeMeshes.cpp +++ b/code/OptimizeMeshes.cpp @@ -47,22 +47,28 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS using namespace Assimp; + #include "OptimizeMeshes.h" #include "ProcessHelper.h" #include "SceneCombiner.h" +static const unsigned int NotSet = 0xffffffff; +static const unsigned int DeadBeef = 0xdeadbeef; + // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer OptimizeMeshesProcess::OptimizeMeshesProcess() : pts (false) -, max_verts (0xffffffff) -, max_faces (0xffffffff) -{} +, max_verts( NotSet ) +, max_faces( NotSet ) { + // empty +} // ------------------------------------------------------------------------------------------------ // Destructor, private as well -OptimizeMeshesProcess::~OptimizeMeshesProcess() -{} +OptimizeMeshesProcess::~OptimizeMeshesProcess() { + // empty +} // ------------------------------------------------------------------------------------------------ // Returns whether the processing step is present in the given flag field. @@ -74,17 +80,17 @@ bool OptimizeMeshesProcess::IsActive( unsigned int pFlags) const // That's a serious design flaw, consider redesign. if( 0 != (pFlags & aiProcess_OptimizeMeshes) ) { pts = (0 != (pFlags & aiProcess_SortByPType)); - max_verts = (0 != (pFlags & aiProcess_SplitLargeMeshes)) ? 0xdeadbeef : max_verts; + max_verts = ( 0 != ( pFlags & aiProcess_SplitLargeMeshes ) ) ? DeadBeef : max_verts; return true; } return false; } // ------------------------------------------------------------------------------------------------ -// Setup properties for the postprocessing step +// Setup properties for the post-processing step void OptimizeMeshesProcess::SetupProperties(const Importer* pImp) { - if (max_verts == 0xdeadbeef /* magic hack */) { + if( max_verts == DeadBeef /* magic hack */ ) { max_faces = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT,AI_SLM_DEFAULT_MAX_TRIANGLES); max_verts = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT,AI_SLM_DEFAULT_MAX_VERTICES); } @@ -104,35 +110,36 @@ void OptimizeMeshesProcess::Execute( aiScene* pScene) mScene = pScene; // need to clear persistent members from previous runs - merge_list.clear(); - output.clear(); + merge_list.resize( 0 ); + output.resize( 0 ); + // ensure we have the right sizes merge_list.reserve(pScene->mNumMeshes); output.reserve(pScene->mNumMeshes); // Prepare lookup tables meshes.resize(pScene->mNumMeshes); FindInstancedMeshes(pScene->mRootNode); - if (max_verts == 0xdeadbeef) /* undo the magic hack */ - max_verts = 0xffffffff; + if( max_verts == DeadBeef ) /* undo the magic hack */ + max_verts = NotSet; // ... instanced meshes are immediately processed and added to the output list for (unsigned int i = 0, n = 0; i < pScene->mNumMeshes;++i) { meshes[i].vertex_format = GetMeshVFormatUnique(pScene->mMeshes[i]); - if (meshes[i].instance_cnt > 1 && meshes[i].output_id == 0xffffffff) { + if (meshes[i].instance_cnt > 1 && meshes[i].output_id == NotSet ) { meshes[i].output_id = n++; output.push_back(mScene->mMeshes[i]); } } - // and process all nodes in the scenegraoh recursively + // and process all nodes in the scenegraph recursively ProcessNode(pScene->mRootNode); if (!output.size()) { throw DeadlyImportError("OptimizeMeshes: No meshes remaining; there's definitely something wrong"); } - meshes.clear(); + meshes.resize( 0 ); ai_assert(output.size() <= num_old); mScene->mNumMeshes = output.size(); @@ -142,8 +149,9 @@ void OptimizeMeshesProcess::Execute( aiScene* pScene) char tmp[512]; ::sprintf(tmp,"OptimizeMeshesProcess finished. Input meshes: %i, Output meshes: %i",num_old,pScene->mNumMeshes); DefaultLogger::get()->info(tmp); - } - else DefaultLogger::get()->debug("OptimizeMeshesProcess finished"); + } else { + DefaultLogger::get()->debug( "OptimizeMeshesProcess finished" ); + } } // ------------------------------------------------------------------------------------------------ @@ -157,7 +165,7 @@ void OptimizeMeshesProcess::ProcessNode( aiNode* pNode) im = meshes[im].output_id; } else { - merge_list.clear(); + merge_list.resize( 0 ); unsigned int verts = 0, faces = 0; // Find meshes to merge with us @@ -170,8 +178,9 @@ void OptimizeMeshesProcess::ProcessNode( aiNode* pNode) faces += mScene->mMeshes[am]->mNumFaces; --pNode->mNumMeshes; - for (unsigned int n = a; n < pNode->mNumMeshes; ++n) - pNode->mMeshes[n] = pNode->mMeshes[n+1]; + for( unsigned int n = a; n < pNode->mNumMeshes; ++n ) { + pNode->mMeshes[ n ] = pNode->mMeshes[ n + 1 ]; + } --a; } @@ -184,8 +193,7 @@ void OptimizeMeshesProcess::ProcessNode( aiNode* pNode) aiMesh* out; SceneCombiner::MergeMeshes(&out,0,merge_list.begin(),merge_list.end()); output.push_back(out); - } - else { + } else { output.push_back(mScene->mMeshes[im]); } im = output.size()-1; @@ -193,8 +201,9 @@ void OptimizeMeshesProcess::ProcessNode( aiNode* pNode) } - for (unsigned int i = 0; i < pNode->mNumChildren; ++i) - ProcessNode(pNode->mChildren[i]); + for( unsigned int i = 0; i < pNode->mNumChildren; ++i ) { + ProcessNode( pNode->mChildren[ i ] ); + } } // ------------------------------------------------------------------------------------------------ @@ -206,8 +215,8 @@ bool OptimizeMeshesProcess::CanJoin ( unsigned int a, unsigned int b, unsigned i aiMesh* ma = mScene->mMeshes[a], *mb = mScene->mMeshes[b]; - if ((0xffffffff != max_verts && verts+mb->mNumVertices > max_verts) || - (0xffffffff != max_faces && faces+mb->mNumFaces > max_faces)) { + if ((NotSet != max_verts && verts+mb->mNumVertices > max_verts) || + (NotSet != max_faces && faces+mb->mNumFaces > max_faces)) { return false; } @@ -221,7 +230,7 @@ bool OptimizeMeshesProcess::CanJoin ( unsigned int a, unsigned int b, unsigned i return false; // If both meshes are skinned, check whether we have many bones defined in both meshes. - // If yes, we can savely join them. + // If yes, we can join them. if (ma->HasBones()) { // TODO return false; @@ -230,14 +239,18 @@ bool OptimizeMeshesProcess::CanJoin ( unsigned int a, unsigned int b, unsigned i } // ------------------------------------------------------------------------------------------------ -// Buidl a LUT of all instanced meshes +// Build a LUT of all instanced meshes void OptimizeMeshesProcess::FindInstancedMeshes (aiNode* pNode) { - for (unsigned int i = 0; i < pNode->mNumMeshes;++i) - ++meshes[pNode->mMeshes[i]].instance_cnt; + for( unsigned int i = 0; i < pNode->mNumMeshes; ++i ) { + ++meshes[ pNode->mMeshes[ i ] ].instance_cnt; + } - for (unsigned int i = 0; i < pNode->mNumChildren; ++i) - FindInstancedMeshes(pNode->mChildren[i]); + for( unsigned int i = 0; i < pNode->mNumChildren; ++i ) { + FindInstancedMeshes( pNode->mChildren[ i ] ); + } } +// ------------------------------------------------------------------------------------------------ + #endif // !! ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS diff --git a/code/SceneCombiner.cpp b/code/SceneCombiner.cpp index d6b138d74..f9face9b1 100644 --- a/code/SceneCombiner.cpp +++ b/code/SceneCombiner.cpp @@ -38,6 +38,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ +// TODO: refactor entire file to get rid of the "flat-copy" first approach +// to copying structures. This easily breaks in the most unintuitive way +// possible as new fields are added to assimp structures. // ---------------------------------------------------------------------------- /** @file Implements Assimp::SceneCombiner. This is a smart utility @@ -1194,10 +1197,53 @@ void SceneCombiner::Copy (aiNode** _dest, const aiNode* src) // get a flat copy ::memcpy(dest,src,sizeof(aiNode)); + if (src->mMetaData) { + Copy(&dest->mMetaData, src->mMetaData); + } + // and reallocate all arrays GetArrayCopy( dest->mMeshes, dest->mNumMeshes ); CopyPtrArray( dest->mChildren, src->mChildren,dest->mNumChildren); } +// ------------------------------------------------------------------------------------------------ +void SceneCombiner::Copy (aiMetadata** _dest, const aiMetadata* src) +{ + ai_assert(NULL != _dest && NULL != src); + + aiMetadata* dest = *_dest = new aiMetadata(); + dest->mNumProperties = src->mNumProperties; + dest->mKeys = new aiString[src->mNumProperties]; + std::copy(src->mKeys, src->mKeys + src->mNumProperties, dest->mKeys); + + dest->mValues = new aiMetadataEntry[src->mNumProperties]; + for (unsigned int i = 0; i < src->mNumProperties; ++i) { + aiMetadataEntry& in = src->mValues[i]; + aiMetadataEntry& out = dest->mValues[i]; + out.mType = in.mType; + switch (dest->mValues[i].mType) { + case AI_BOOL: + out.mData = new bool(*static_cast(in.mData)); + break; + case AI_INT: + out.mData = new int(*static_cast(in.mData)); + break; + case AI_UINT64: + out.mData = new uint64_t(*static_cast(in.mData)); + break; + case AI_FLOAT: + out.mData = new float(*static_cast(in.mData)); + break; + case AI_AISTRING: + out.mData = new aiString(*static_cast(in.mData)); + break; + case AI_AIVECTOR3D: + out.mData = new aiVector3D(*static_cast(in.mData)); + break; + default: + ai_assert(false); + } + } +} } diff --git a/code/SceneCombiner.h b/code/SceneCombiner.h index e7dd242fd..0c4cfc2ee 100644 --- a/code/SceneCombiner.h +++ b/code/SceneCombiner.h @@ -349,6 +349,7 @@ public: static void Copy (aiBone** dest, const aiBone* src); static void Copy (aiLight** dest, const aiLight* src); static void Copy (aiNodeAnim** dest, const aiNodeAnim* src); + static void Copy (aiMetadata** dest, const aiMetadata* src); // recursive, of course static void Copy (aiNode** dest, const aiNode* src); diff --git a/code/XFileExporter.cpp b/code/XFileExporter.cpp new file mode 100644 index 000000000..2cd3c4d37 --- /dev/null +++ b/code/XFileExporter.cpp @@ -0,0 +1,517 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +@author: Richard Steffen, 2014 +---------------------------------------------------------------------- +*/ + +#include "AssimpPCH.h" + +#ifndef ASSIMP_BUILD_NO_EXPORT +#ifndef ASSIMP_BUILD_NO_XFILE_EXPORTER +#include "XFileExporter.h" +#include "ConvertToLHProcess.h" +#include "Bitmap.h" +#include "fast_atof.h" +#include "SceneCombiner.h" + +#include +#include + +using namespace Assimp; + +namespace Assimp +{ + +// ------------------------------------------------------------------------------------------------ +// Worker function for exporting a scene to Collada. Prototyped and registered in Exporter.cpp +void ExportSceneXFile(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene) +{ + std::string path = ""; + std::string file = pFile; + + // We need to test both types of folder separators because pIOSystem->getOsSeparator() is not reliable. + // Moreover, the path given by some applications is not even consistent with the OS specific type of separator. + const char* end_path = std::max(strrchr(pFile, '\\'), strrchr(pFile, '/')); + + if(end_path != NULL) { + path = std::string(pFile, end_path + 1 - pFile); + file = file.substr(end_path + 1 - pFile, file.npos); + + std::size_t pos = file.find_last_of('.'); + if(pos != file.npos) { + file = file.substr(0, pos); + } + } + + // invoke the exporter + XFileExporter iDoTheExportThing( pScene, pIOSystem, path, file); + + // we're still here - export successfully completed. Write result to the given IOSYstem + boost::scoped_ptr outfile (pIOSystem->Open(pFile,"wt")); + if(outfile == NULL) { + throw DeadlyExportError("could not open output .dae file: " + std::string(pFile)); + } + + // XXX maybe use a small wrapper around IOStream that behaves like std::stringstream in order to avoid the extra copy. + outfile->Write( iDoTheExportThing.mOutput.str().c_str(), static_cast(iDoTheExportThing.mOutput.tellp()),1); +} + +} // end of namespace Assimp + + +// ------------------------------------------------------------------------------------------------ +// Constructor for a specific scene to export +XFileExporter::XFileExporter(const aiScene* pScene, IOSystem* pIOSystem, const std::string& path, const std::string& file) : mIOSystem(pIOSystem), mPath(path), mFile(file) +{ + // make sure that all formatting happens using the standard, C locale and not the user's current locale + mOutput.imbue( std::locale("C") ); + + mScene = pScene; + mSceneOwned = false; + + // set up strings + endstr = "\n"; + + // start writing + WriteFile(); +} + +// ------------------------------------------------------------------------------------------------ +// Destructor +XFileExporter::~XFileExporter() +{ + if(mSceneOwned) { + delete mScene; + } +} + +// ------------------------------------------------------------------------------------------------ +// Starts writing the contents +void XFileExporter::WriteFile() +{ + // note, that all realnumber values must be comma separated in x files + mOutput.setf(std::ios::fixed); + mOutput.precision(6); // precission for float + + // entry of writing the file + WriteHeader(); + + mOutput << startstr << "Frame DXCC_ROOT {" << endstr; + PushTag(); + + aiMatrix4x4 I; // identity + WriteFrameTransform(I); + + WriteNode(mScene->mRootNode); + PopTag(); + + mOutput << startstr << "}" << endstr; + +} + +// ------------------------------------------------------------------------------------------------ +// Writes the asset header +void XFileExporter::WriteHeader() +{ + mOutput << startstr << "xof 0303txt 0032" << endstr; + mOutput << endstr; + mOutput << startstr << "template Frame {" << endstr; + PushTag(); + mOutput << startstr << "<3d82ab46-62da-11cf-ab39-0020af71e433>" << endstr; + mOutput << startstr << "[...]" << endstr; + PopTag(); + mOutput << startstr << "}" << endstr; + mOutput << endstr; + mOutput << startstr << "template Matrix4x4 {" << endstr; + PushTag(); + mOutput << startstr << "" << endstr; + mOutput << startstr << "array FLOAT matrix[16];" << endstr; + PopTag(); + mOutput << startstr << "}" << endstr; + mOutput << endstr; + mOutput << startstr << "template FrameTransformMatrix {" << endstr; + PushTag(); + mOutput << startstr << "" << endstr; + mOutput << startstr << "Matrix4x4 frameMatrix;" << endstr; + PopTag(); + mOutput << startstr << "}" << endstr; + mOutput << endstr; + mOutput << startstr << "template Vector {" << endstr; + PushTag(); + mOutput << startstr << "<3d82ab5e-62da-11cf-ab39-0020af71e433>" << endstr; + mOutput << startstr << "FLOAT x;" << endstr; + mOutput << startstr << "FLOAT y;" << endstr; + mOutput << startstr << "FLOAT z;" << endstr; + PopTag(); + mOutput << startstr << "}" << endstr; + mOutput << endstr; + mOutput << startstr << "template MeshFace {" << endstr; + PushTag(); + mOutput << startstr << "<3d82ab5f-62da-11cf-ab39-0020af71e433>" << endstr; + mOutput << startstr << "DWORD nFaceVertexIndices;" << endstr; + mOutput << startstr << "array DWORD faceVertexIndices[nFaceVertexIndices];" << endstr; + PopTag(); + mOutput << startstr << "}" << endstr; + mOutput << endstr; + mOutput << startstr << "template Mesh {" << endstr; + PushTag(); + mOutput << startstr << "<3d82ab44-62da-11cf-ab39-0020af71e433>" << endstr; + mOutput << startstr << "DWORD nVertices;" << endstr; + mOutput << startstr << "array Vector vertices[nVertices];" << endstr; + mOutput << startstr << "DWORD nFaces;" << endstr; + mOutput << startstr << "array MeshFace faces[nFaces];" << endstr; + mOutput << startstr << "[...]" << endstr; + PopTag(); + mOutput << startstr << "}" << endstr; + mOutput << endstr; + mOutput << startstr << "template MeshNormals {" << endstr; + PushTag(); + mOutput << startstr << "" << endstr; + mOutput << startstr << "DWORD nNormals;" << endstr; + mOutput << startstr << "array Vector normals[nNormals];" << endstr; + mOutput << startstr << "DWORD nFaceNormals;" << endstr; + mOutput << startstr << "array MeshFace faceNormals[nFaceNormals];" << endstr; + PopTag(); + mOutput << startstr << "}" << endstr; + mOutput << endstr; + mOutput << startstr << "template Coords2d {" << endstr; + PushTag(); + mOutput << startstr << "" << endstr; + mOutput << startstr << "FLOAT u;" << endstr; + mOutput << startstr << "FLOAT v;" << endstr; + PopTag(); + mOutput << startstr << "}" << endstr; + mOutput << endstr; + mOutput << startstr << "template MeshTextureCoords {" << endstr; + PushTag(); + mOutput << startstr << "" << endstr; + mOutput << startstr << "DWORD nTextureCoords;" << endstr; + mOutput << startstr << "array Coords2d textureCoords[nTextureCoords];" << endstr; + PopTag(); + mOutput << startstr << "}" << endstr; + mOutput << endstr; + mOutput << startstr << "template ColorRGBA {" << endstr; + PushTag(); + mOutput << startstr << "<35ff44e0-6c7c-11cf-8f52-0040333594a3>" << endstr; + mOutput << startstr << "FLOAT red;" << endstr; + mOutput << startstr << "FLOAT green;" << endstr; + mOutput << startstr << "FLOAT blue;" << endstr; + mOutput << startstr << "FLOAT alpha;" << endstr; + PopTag(); + mOutput << startstr << "}" << endstr; + mOutput << endstr; + mOutput << startstr << "template IndexedColor {" << endstr; + PushTag(); + mOutput << startstr << "<1630b820-7842-11cf-8f52-0040333594a3>" << endstr; + mOutput << startstr << "DWORD index;" << endstr; + mOutput << startstr << "ColorRGBA indexColor;" << endstr; + PopTag(); + mOutput << startstr << "}" << endstr; + mOutput << endstr; + mOutput << startstr << "template MeshVertexColors {" << endstr; + PushTag(); + mOutput << startstr << "<1630b821-7842-11cf-8f52-0040333594a3>" << endstr; + mOutput << startstr << "DWORD nVertexColors;" << endstr; + mOutput << startstr << "array IndexedColor vertexColors[nVertexColors];" << endstr; + PopTag(); + mOutput << startstr << "}" << endstr; + mOutput << endstr; + mOutput << startstr << "template VertexElement {" << endstr; + PushTag(); + mOutput << startstr << "" << endstr; + mOutput << startstr << "DWORD Type;" << endstr; + mOutput << startstr << "DWORD Method;" << endstr; + mOutput << startstr << "DWORD Usage;" << endstr; + mOutput << startstr << "DWORD UsageIndex;" << endstr; + PopTag(); + mOutput << startstr << "}" << endstr; + mOutput << endstr; + mOutput << startstr << "template DeclData {" << endstr; + PushTag(); + mOutput << startstr << "" << endstr; + mOutput << startstr << "DWORD nElements;" << endstr; + mOutput << startstr << "array VertexElement Elements[nElements];" << endstr; + mOutput << startstr << "DWORD nDWords;" << endstr; + mOutput << startstr << "array DWORD data[nDWords];" << endstr; + PopTag(); + mOutput << startstr << "}" << endstr; + mOutput << endstr; +} + + +// Writes the material setup +void XFileExporter::WriteFrameTransform(aiMatrix4x4& m) +{ + mOutput << startstr << "FrameTransformMatrix {" << endstr << " "; + PushTag(); + mOutput << startstr << m.a1 << ", " << m.b1 << ", " << m.c1 << ", " << m.d1 << "," << endstr; + mOutput << startstr << m.a2 << ", " << m.b2 << ", " << m.c2 << ", " << m.d2 << "," << endstr; + mOutput << startstr << m.a3 << ", " << m.b3 << ", " << m.c3 << ", " << m.d3 << "," << endstr; + mOutput << startstr << m.a4 << ", " << m.b4 << ", " << m.c4 << ", " << m.d4 << ";;" << endstr; + PopTag(); + mOutput << startstr << "}" << endstr << endstr; +} + + +// ------------------------------------------------------------------------------------------------ +// Recursively writes the given node +void XFileExporter::WriteNode( aiNode* pNode) +{ + if (pNode->mName.length==0) + { + std::stringstream ss; + ss << "Node_" << pNode; + pNode->mName.Set(ss.str()); + } + mOutput << startstr << "Frame " << pNode->mName.C_Str() << " {" << endstr; + + PushTag(); + + aiMatrix4x4 m = pNode->mTransformation; + + WriteFrameTransform(m); + + for (size_t i = 0; i < pNode->mNumMeshes; i++) + WriteMesh(mScene->mMeshes[pNode->mMeshes[i]]); + + // recursive call the Nodes + for (size_t a = 0; a < pNode->mNumChildren; a++) + WriteNode( mScene->mRootNode->mChildren[a]); + + PopTag(); + + mOutput << startstr << "}" << endstr << endstr; +} + +void XFileExporter::WriteMesh(const aiMesh* mesh) +{ + mOutput << startstr << "Mesh " << mesh->mName.C_Str() << "_mShape" << " {" << endstr; + + PushTag(); + + // write all the vertices + mOutput << startstr << mesh->mNumVertices << ";" << endstr; + for (size_t a = 0; a < mesh->mNumVertices; a++) + { + aiVector3D &v = mesh->mVertices[a]; + mOutput << startstr << v[0] << ";"<< v[1] << ";" << v[2] << ";"; + if (a < mesh->mNumVertices - 1) + mOutput << "," << endstr; + else + mOutput << ";" << endstr; + } + + // write all the faces + mOutput << startstr << mesh->mNumFaces << ";" << endstr; + for( size_t a = 0; a < mesh->mNumFaces; ++a ) + { + const aiFace& face = mesh->mFaces[a]; + mOutput << startstr << face.mNumIndices << ";"; + // must be counter clockwise triangle + //for(int b = face.mNumIndices - 1; b >= 0 ; --b) + for(size_t b = 0; b < face.mNumIndices ; ++b) + { + mOutput << face.mIndices[b]; + //if (b > 0) + if (bmNumFaces - 1) + mOutput << "," << endstr; + else + mOutput << ";" << endstr; + } + + mOutput << endstr; + + if (mesh->HasTextureCoords(0)) + { + const aiMaterial* mat = mScene->mMaterials[mesh->mMaterialIndex]; + aiString relpath; + mat->Get(_AI_MATKEY_TEXTURE_BASE, aiTextureType_DIFFUSE, 0, relpath); + + mOutput << startstr << "MeshMaterialList {" << endstr; + PushTag(); + mOutput << startstr << "1;" << endstr; // number of materials + mOutput << startstr << mesh->mNumFaces << ";" << endstr; // number of faces + mOutput << startstr; + for( size_t a = 0; a < mesh->mNumFaces; ++a ) + { + mOutput << "0"; // the material index + if (a < mesh->mNumFaces - 1) + mOutput << ", "; + else + mOutput << ";" << endstr; + } + mOutput << startstr << "Material {" << endstr; + PushTag(); + mOutput << startstr << "1.0; 1.0; 1.0; 1.000000;;" << endstr; + mOutput << startstr << "1.000000;" << endstr; // power + mOutput << startstr << "0.000000; 0.000000; 0.000000;;" << endstr; // specularity + mOutput << startstr << "0.000000; 0.000000; 0.000000;;" << endstr; // emission + mOutput << startstr << "TextureFilename { \""; + + writePath(relpath); + + mOutput << "\"; }" << endstr; + PopTag(); + mOutput << startstr << "}" << endstr; + PopTag(); + mOutput << startstr << "}" << endstr; + } + + // write normals (every vertex has one) + if (mesh->HasNormals()) + { + mOutput << endstr << startstr << "MeshNormals {" << endstr; + mOutput << startstr << mesh->mNumVertices << ";" << endstr; + for (size_t a = 0; a < mesh->mNumVertices; a++) + { + aiVector3D &v = mesh->mNormals[a]; + // because we have a LHS and also changed wth winding, we need to invert the normals again + mOutput << startstr << -v[0] << ";"<< -v[1] << ";" << -v[2] << ";"; + if (a < mesh->mNumVertices - 1) + mOutput << "," << endstr; + else + mOutput << ";" << endstr; + } + + mOutput << startstr << mesh->mNumFaces << ";" << endstr; + for (size_t a = 0; a < mesh->mNumFaces; a++) + { + const aiFace& face = mesh->mFaces[a]; + mOutput << startstr << face.mNumIndices << ";"; + + //for(int b = face.mNumIndices-1; b >= 0 ; --b) + for(size_t b = 0; b < face.mNumIndices ; ++b) + { + mOutput << face.mIndices[b]; + //if (b > 0) + if (bmNumFaces-1) + mOutput << "," << endstr; + else + mOutput << ";" << endstr; + } + mOutput << startstr << "}" << endstr; + } + + // write texture UVs if available + if (mesh->HasTextureCoords(0)) + { + mOutput << endstr << startstr << "MeshTextureCoords {" << endstr; + mOutput << startstr << mesh->mNumVertices << ";" << endstr; + for (size_t a = 0; a < mesh->mNumVertices; a++) + //for (int a = (int)mesh->mNumVertices-1; a >=0 ; a--) + { + aiVector3D& uv = mesh->mTextureCoords[0][a]; // uv of first uv layer for the vertex + mOutput << startstr << uv.x << ";" << uv.y; + if (a < mesh->mNumVertices-1) + //if (a >0 ) + mOutput << ";," << endstr; + else + mOutput << ";;" << endstr; + } + mOutput << startstr << "}" << endstr; + } + + // write color channel if available + if (mesh->HasVertexColors(0)) + { + mOutput << endstr << startstr << "MeshVertexColors {" << endstr; + mOutput << startstr << mesh->mNumVertices << ";" << endstr; + for (size_t a = 0; a < mesh->mNumVertices; a++) + { + aiColor4D& mColors = mesh->mColors[0][a]; // color of first vertex color set for the vertex + mOutput << startstr << a << ";" << mColors.r << ";" << mColors.g << ";" << mColors.b << ";" << mColors.a << ";;"; + if (a < mesh->mNumVertices-1) + mOutput << "," << endstr; + else + mOutput << ";" << endstr; + } + mOutput << startstr << "}" << endstr; + } + /* + else + { + mOutput << endstr << startstr << "MeshVertexColors {" << endstr; + mOutput << startstr << mesh->mNumVertices << ";" << endstr; + for (size_t a = 0; a < mesh->mNumVertices; a++) + { + aiColor4D* mColors = mesh->mColors[a]; + mOutput << startstr << a << ";0.500000;0.000000;0.000000;0.500000;;"; + if (a < mesh->mNumVertices-1) + mOutput << "," << endstr; + else + mOutput << ";" << endstr; + } + mOutput << startstr << "}" << endstr; + } + */ + PopTag(); + mOutput << startstr << "}" << endstr << endstr; + +} + + +void XFileExporter::writePath(aiString path) +{ + std::string str = std::string(path.C_Str()); + BaseImporter::ConvertUTF8toISO8859_1(str); + + while( str.find( "\\\\") != std::string::npos) + str.replace( str.find( "\\\\"), 2, "\\"); + + while( str.find( "\\") != std::string::npos) + str.replace( str.find( "\\"), 1, "/"); + + mOutput << str; + +} + +#endif +#endif + diff --git a/code/XFileExporter.h b/code/XFileExporter.h new file mode 100644 index 000000000..fa4e6d38e --- /dev/null +++ b/code/XFileExporter.h @@ -0,0 +1,125 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2012, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +@author: Richard Steffen, 2014 + +---------------------------------------------------------------------- +*/ + +/** @file XFileExporter.h + * Declares the exporter class to write a scene to a Collada file + */ +#ifndef AI_XFILEEXPORTER_H_INC +#define AI_XFILEEXPORTER_H_INC + +#include "../include/assimp/ai_assert.h" +#include + +struct aiScene; +struct aiNode; + +namespace Assimp +{ + +/// Helper class to export a given scene to a X-file. +/// Note: an xFile uses a left hand system. Assimp used a right hand system (OpenGL), therefore we have to transform everything +class XFileExporter +{ +public: + /// Constructor for a specific scene to export + XFileExporter(const aiScene* pScene, IOSystem* pIOSystem, const std::string& path, const std::string& file); + + /// Destructor + virtual ~XFileExporter(); + +protected: + /// Starts writing the contents + void WriteFile(); + + /// Writes the asset header + void WriteHeader(); + + /// write a frame transform + void WriteFrameTransform(aiMatrix4x4& m); + + /// Recursively writes the given node + void WriteNode( aiNode* pNode ); + + /// write a mesh entry of the scene + void WriteMesh(const aiMesh* mesh); + + /// Enters a new xml element, which increases the indentation + void PushTag() { startstr.append( " "); } + + /// Leaves an element, decreasing the indentation + void PopTag() { ai_assert( startstr.length() > 1); startstr.erase( startstr.length() - 2); } + +public: + /// Stringstream to write all output into + std::stringstream mOutput; + +protected: + + /// write a path + void writePath(aiString path); + + /// The IOSystem for output + IOSystem* mIOSystem; + + /// Path of the directory where the scene will be exported + const std::string mPath; + + /// Name of the file (without extension) where the scene will be exported + const std::string mFile; + + /// The scene to be written + const aiScene* mScene; + bool mSceneOwned; + + /// current line start string, contains the current indentation for simple stream insertion + std::string startstr; + + /// current line end string for simple stream insertion + std::string endstr; + +protected: + +}; + +} + +#endif // !! AI_XFILEEXPORTER_H_INC diff --git a/doc/AssimpCmdDoc_Html/AssimpCmdDoc.chm b/doc/AssimpCmdDoc_Html/AssimpCmdDoc.chm index c81fa6e53..5bac786ff 100644 Binary files a/doc/AssimpCmdDoc_Html/AssimpCmdDoc.chm and b/doc/AssimpCmdDoc_Html/AssimpCmdDoc.chm differ diff --git a/doc/AssimpDoc_Html/AssimpDoc.chm b/doc/AssimpDoc_Html/AssimpDoc.chm index 66a0b7446..ce8c91c99 100644 Binary files a/doc/AssimpDoc_Html/AssimpDoc.chm and b/doc/AssimpDoc_Html/AssimpDoc.chm differ diff --git a/doc/Doxyfile b/doc/Doxyfile index af1f8f0fe..64dd5d2cc 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -1,298 +1,1900 @@ -# Doxyfile 1.5.8 - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- -DOXYFILE_ENCODING = UTF-8 -PROJECT_NAME = Assimp -PROJECT_NUMBER = "v2.0 (November 2010)" -OUTPUT_DIRECTORY = -CREATE_SUBDIRS = NO -OUTPUT_LANGUAGE = English -BRIEF_MEMBER_DESC = YES -REPEAT_BRIEF = YES -ABBREVIATE_BRIEF = "The $name class " \ - "The $name widget " \ - "The $name file " \ - is \ - provides \ - specifies \ - contains \ - represents \ - a \ - an \ - the -ALWAYS_DETAILED_SEC = NO -INLINE_INHERITED_MEMB = NO -FULL_PATH_NAMES = NO -STRIP_FROM_PATH = -STRIP_FROM_INC_PATH = -SHORT_NAMES = NO -JAVADOC_AUTOBRIEF = YES -QT_AUTOBRIEF = NO -MULTILINE_CPP_IS_BRIEF = NO -INHERIT_DOCS = YES -SEPARATE_MEMBER_PAGES = NO -TAB_SIZE = 8 -ALIASES = -OPTIMIZE_OUTPUT_FOR_C = NO -OPTIMIZE_OUTPUT_JAVA = NO -OPTIMIZE_FOR_FORTRAN = NO -OPTIMIZE_OUTPUT_VHDL = NO -EXTENSION_MAPPING = -BUILTIN_STL_SUPPORT = YES -CPP_CLI_SUPPORT = NO -SIP_SUPPORT = NO -IDL_PROPERTY_SUPPORT = YES -DISTRIBUTE_GROUP_DOC = NO -SUBGROUPING = YES -TYPEDEF_HIDES_STRUCT = YES -SYMBOL_CACHE_SIZE = 0 -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- -EXTRACT_ALL = YES -EXTRACT_PRIVATE = NO -EXTRACT_STATIC = YES -EXTRACT_LOCAL_CLASSES = YES -EXTRACT_LOCAL_METHODS = NO -EXTRACT_ANON_NSPACES = NO -HIDE_UNDOC_MEMBERS = YES -HIDE_UNDOC_CLASSES = YES -HIDE_FRIEND_COMPOUNDS = NO -HIDE_IN_BODY_DOCS = NO -INTERNAL_DOCS = NO -CASE_SENSE_NAMES = NO -HIDE_SCOPE_NAMES = NO -SHOW_INCLUDE_FILES = NO -INLINE_INFO = YES -SORT_MEMBER_DOCS = YES -SORT_BRIEF_DOCS = YES -SORT_GROUP_NAMES = NO -SORT_BY_SCOPE_NAME = YES -GENERATE_TODOLIST = YES -GENERATE_TESTLIST = YES -GENERATE_BUGLIST = YES -GENERATE_DEPRECATEDLIST= YES -ENABLED_SECTIONS = -MAX_INITIALIZER_LINES = 30 -SHOW_USED_FILES = YES -SHOW_DIRECTORIES = NO -SHOW_FILES = YES -SHOW_NAMESPACES = YES -FILE_VERSION_FILTER = -LAYOUT_FILE = -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- -QUIET = NO -WARNINGS = YES -WARN_IF_UNDOCUMENTED = YES -WARN_IF_DOC_ERROR = YES -WARN_NO_PARAMDOC = NO -WARN_FORMAT = "$file:$line: $text" -WARN_LOGFILE = -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- -INPUT = ../include/ \ - ../doc/dox.h \ - ../code/BaseImporter.h -INPUT_ENCODING = UTF-8 -FILE_PATTERNS = *.c \ - *.cc \ - *.cxx \ - *.cpp \ - *.c++ \ - *.d \ - *.java \ - *.ii \ - *.ixx \ - *.ipp \ - *.i++ \ - *.inl \ - *.h \ - *.hh \ - *.hxx \ - *.hpp \ - *.h++ \ - *.idl \ - *.odl \ - *.cs \ - *.php \ - *.php3 \ - *.inc \ - *.m \ - *.mm \ - *.dox \ - *.py -RECURSIVE = YES -EXCLUDE = -EXCLUDE_SYMLINKS = NO -EXCLUDE_PATTERNS = */.svn/* \ - */.svn -EXCLUDE_SYMBOLS = -EXAMPLE_PATH = -EXAMPLE_PATTERNS = * -EXAMPLE_RECURSIVE = NO -IMAGE_PATH = -INPUT_FILTER = -FILTER_PATTERNS = -FILTER_SOURCE_FILES = NO -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- -SOURCE_BROWSER = NO -INLINE_SOURCES = NO -STRIP_CODE_COMMENTS = YES -REFERENCED_BY_RELATION = NO -REFERENCES_RELATION = NO -REFERENCES_LINK_SOURCE = YES -USE_HTAGS = NO -VERBATIM_HEADERS = NO -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- -ALPHABETICAL_INDEX = YES -COLS_IN_ALPHA_INDEX = 5 -IGNORE_PREFIX = -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- -GENERATE_HTML = YES -HTML_OUTPUT = AssimpDoc_Html -HTML_FILE_EXTENSION = .html -HTML_HEADER = -HTML_FOOTER = -HTML_STYLESHEET = style.css -HTML_ALIGN_MEMBERS = YES -HTML_DYNAMIC_SECTIONS = NO -GENERATE_DOCSET = NO -DOCSET_FEEDNAME = "Doxygen generated docs" -DOCSET_BUNDLE_ID = org.doxygen.Project -GENERATE_HTMLHELP = YES -CHM_FILE = AssimpDoc.chm -HHC_LOCATION = "C:\Program Files (x86)\HTML Help Workshop/hhc.exe" -GENERATE_CHI = NO -CHM_INDEX_ENCODING = -BINARY_TOC = NO -TOC_EXPAND = NO -GENERATE_QHP = NO -QCH_FILE = -QHP_NAMESPACE = -QHP_VIRTUAL_FOLDER = doc -QHP_CUST_FILTER_NAME = -QHP_CUST_FILTER_ATTRS = -QHP_SECT_FILTER_ATTRS = -QHG_LOCATION = -DISABLE_INDEX = NO -ENUM_VALUES_PER_LINE = 4 -GENERATE_TREEVIEW = YES -TREEVIEW_WIDTH = 250 -FORMULA_FONTSIZE = 10 -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- -GENERATE_LATEX = NO -LATEX_OUTPUT = latex -LATEX_CMD_NAME = latex -MAKEINDEX_CMD_NAME = makeindex -COMPACT_LATEX = NO -PAPER_TYPE = a4wide -EXTRA_PACKAGES = -LATEX_HEADER = -PDF_HYPERLINKS = NO -USE_PDFLATEX = NO -LATEX_BATCHMODE = NO -LATEX_HIDE_INDICES = NO -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- -GENERATE_RTF = NO -RTF_OUTPUT = rtf -COMPACT_RTF = NO -RTF_HYPERLINKS = NO -RTF_STYLESHEET_FILE = -RTF_EXTENSIONS_FILE = -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- -GENERATE_MAN = NO -MAN_OUTPUT = man -MAN_EXTENSION = .3 -MAN_LINKS = NO -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- -GENERATE_XML = NO -XML_OUTPUT = xml -XML_SCHEMA = -XML_DTD = -XML_PROGRAMLISTING = YES -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- -GENERATE_AUTOGEN_DEF = NO -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- -GENERATE_PERLMOD = NO -PERLMOD_LATEX = NO -PERLMOD_PRETTY = YES -PERLMOD_MAKEVAR_PREFIX = -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- -ENABLE_PREPROCESSING = YES -MACRO_EXPANSION = YES -EXPAND_ONLY_PREDEF = YES -SEARCH_INCLUDES = YES -INCLUDE_PATH = -INCLUDE_FILE_PATTERNS = -PREDEFINED = ASSIMP_DOXYGEN_BUILD=1 \ - __cplusplus -EXPAND_AS_DEFINED = C_STRUCT \ - C_ENUM -SKIP_FUNCTION_MACROS = YES -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- -TAGFILES = -GENERATE_TAGFILE = -ALLEXTERNALS = NO -EXTERNAL_GROUPS = YES -PERL_PATH = /usr/bin/perl -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- -CLASS_DIAGRAMS = NO -MSCGEN_PATH = -HIDE_UNDOC_RELATIONS = YES -HAVE_DOT = NO -DOT_FONTNAME = FreeSans -DOT_FONTSIZE = 10 -DOT_FONTPATH = -CLASS_GRAPH = YES -COLLABORATION_GRAPH = YES -GROUP_GRAPHS = YES -UML_LOOK = NO -TEMPLATE_RELATIONS = NO -INCLUDE_GRAPH = YES -INCLUDED_BY_GRAPH = YES -CALL_GRAPH = NO -CALLER_GRAPH = NO -GRAPHICAL_HIERARCHY = YES -DIRECTORY_GRAPH = YES -DOT_IMAGE_FORMAT = png -DOT_PATH = -DOTFILE_DIRS = -DOT_GRAPH_MAX_NODES = 50 -MAX_DOT_GRAPH_DEPTH = 1000 -DOT_TRANSPARENT = NO -DOT_MULTI_TARGETS = NO -GENERATE_LEGEND = YES -DOT_CLEANUP = YES -#--------------------------------------------------------------------------- -# Options related to the search engine -#--------------------------------------------------------------------------- -SEARCHENGINE = NO +# Doxyfile 1.8.3.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = Assimp + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = "v3.1.1 (June 2014)" + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = "The $name class " \ + "The $name widget " \ + "The $name file " \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. Note that you specify absolute paths here, but also +# relative paths, which will be relative from the directory where doxygen is +# started. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, +# and language is one of the parsers supported by doxygen: IDL, Java, +# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, +# C++. For instance to make doxygen treat .inc files as Fortran files (default +# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note +# that for custom extensions you also need to set FILE_PATTERNS otherwise the +# files are not read by doxygen. + +EXTENSION_MAPPING = + +# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all +# comments according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you +# can mix doxygen, HTML, and XML commands with Markdown formatting. +# Disable only in case of backward compatibilities issues. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented classes, +# or namespaces to their corresponding documentation. Such a link can be +# prevented in individual cases by by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES (the +# default) will make doxygen replace the get and set methods by a property in +# the documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields will be shown inline in the documentation +# of the scope in which they are defined (i.e. file, namespace, or group +# documentation), provided this scope is documented. If set to NO (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man +# pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = YES + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +SYMBOL_CACHE_SIZE = 0 + +# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be +# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given +# their name and scope. Since this can be an expensive process and often the +# same symbol appear multiple times in the code, doxygen keeps a cache of +# pre-resolved symbols. If the cache is too small doxygen will become slower. +# If the cache is too large, memory is wasted. The cache size is given by this +# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = YES + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = YES + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = YES + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if section-label ... \endif +# and \cond section-label ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. Do not use +# file names with spaces, bibtex cannot handle them. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = ../include/ \ + ../doc/dox.h \ + ../code/BaseImporter.h + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox \ + *.py + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = */.svn/* \ + */.svn + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page (index.html). +# This can be useful if you have a project on for instance GitHub and want reuse +# the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C, C++ and Fortran comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = AssimpDoc_Html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If left blank doxygen will +# generate a default style sheet. Note that it is recommended to use +# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this +# tag will in the future become obsolete. + +HTML_STYLESHEET = style.css + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional +# user-defined cascading style sheet that is included after the standard +# style sheets created by doxygen. Using this option one can overrule +# certain style aspects. This is preferred over using HTML_STYLESHEET +# since it does not replace the standard style sheet and is therefor more +# robust against future updates. Doxygen will copy the style sheet file to +# the output directory. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of +# entries shown in the various tree structured indices initially; the user +# can expand and collapse entries dynamically later on. Doxygen will expand +# the tree to such a level that at most the specified number of entries are +# visible (unless a fully collapsed tree already exceeds this amount). +# So setting the number of entries 1 will produce a full collapsed tree by +# default. 0 is a special value representing an infinite number of entries +# and will result in a full expanded tree by default. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely +# identify the documentation publisher. This should be a reverse domain-name +# style string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = YES + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = AssimpDoc.chm + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = "C:\Program Files (x86)\HTML Help Workshop/hhc.exe" + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + +GENERATE_TREEVIEW = YES + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you may also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and +# SVG. The default value is HTML-CSS, which is slower, but has the best +# compatibility. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to +# the MathJax Content Delivery Network so you can quickly see the result without +# installing MathJax. However, it is strongly recommended to install a local +# copy of MathJax from http://www.mathjax.org before deployment. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = NO + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. +# There are two flavours of web server based search depending on the +# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for +# searching and an index file used by the script. When EXTERNAL_SEARCH is +# enabled the indexing and searching needs to be provided by external tools. +# See the manual for details. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain +# the search results. Doxygen ships with an example indexer (doxyindexer) and +# search engine (doxysearch.cgi) which are based on the open source search engine +# library Xapian. See the manual for configuration details. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will returned the search results when EXTERNAL_SEARCH is enabled. +# Doxygen ships with an example search engine (doxysearch) which is based on +# the open source search engine library Xapian. See the manual for configuration +# details. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id +# of to a relative location where the documentation can be found. +# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = YES + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = ASSIMP_DOXYGEN_BUILD=1 \ + __cplusplus + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = C_STRUCT \ + C_ENUM + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. For each +# tag file the location of the external documentation should be added. The +# format of a tag file without this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths +# or URLs. Note that each tag file must have a unique name (where the name does +# NOT include the path). If a tag file is not located in the directory in which +# doxygen is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = NO + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = FreeSans + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside +# the class node. If there are many fields or methods and many nodes the +# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS +# threshold limits the number of items for each type to make the size more +# managable. Set this to 0 for no limit. Note that the threshold may be +# exceeded by 50% before the limit is enforced. + +UML_LIMIT_NUM_FIELDS = 10 + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 1000 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/doc/Doxyfile_Cmd b/doc/Doxyfile_Cmd index 237ec3674..f0a56e7c9 100644 --- a/doc/Doxyfile_Cmd +++ b/doc/Doxyfile_Cmd @@ -1,296 +1,2368 @@ -# Doxyfile 1.5.8 - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- -DOXYFILE_ENCODING = UTF-8 -PROJECT_NAME = Assimp Command Line Tools -PROJECT_NUMBER = "v2.0 (November 2010)" -OUTPUT_DIRECTORY = -CREATE_SUBDIRS = NO -OUTPUT_LANGUAGE = English -BRIEF_MEMBER_DESC = YES -REPEAT_BRIEF = YES -ABBREVIATE_BRIEF = "The $name class " \ - "The $name widget " \ - "The $name file " \ - is \ - provides \ - specifies \ - contains \ - represents \ - a \ - an \ - the -ALWAYS_DETAILED_SEC = NO -INLINE_INHERITED_MEMB = NO -FULL_PATH_NAMES = NO -STRIP_FROM_PATH = -STRIP_FROM_INC_PATH = -SHORT_NAMES = NO -JAVADOC_AUTOBRIEF = YES -QT_AUTOBRIEF = NO -MULTILINE_CPP_IS_BRIEF = NO -INHERIT_DOCS = YES -SEPARATE_MEMBER_PAGES = NO -TAB_SIZE = 8 -ALIASES = -OPTIMIZE_OUTPUT_FOR_C = NO -OPTIMIZE_OUTPUT_JAVA = NO -OPTIMIZE_FOR_FORTRAN = NO -OPTIMIZE_OUTPUT_VHDL = NO -EXTENSION_MAPPING = -BUILTIN_STL_SUPPORT = YES -CPP_CLI_SUPPORT = NO -SIP_SUPPORT = NO -IDL_PROPERTY_SUPPORT = YES -DISTRIBUTE_GROUP_DOC = NO -SUBGROUPING = YES -TYPEDEF_HIDES_STRUCT = YES -SYMBOL_CACHE_SIZE = 0 -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- -EXTRACT_ALL = YES -EXTRACT_PRIVATE = NO -EXTRACT_STATIC = YES -EXTRACT_LOCAL_CLASSES = YES -EXTRACT_LOCAL_METHODS = NO -EXTRACT_ANON_NSPACES = NO -HIDE_UNDOC_MEMBERS = YES -HIDE_UNDOC_CLASSES = YES -HIDE_FRIEND_COMPOUNDS = NO -HIDE_IN_BODY_DOCS = NO -INTERNAL_DOCS = NO -CASE_SENSE_NAMES = NO -HIDE_SCOPE_NAMES = NO -SHOW_INCLUDE_FILES = NO -INLINE_INFO = YES -SORT_MEMBER_DOCS = YES -SORT_BRIEF_DOCS = YES -SORT_GROUP_NAMES = NO -SORT_BY_SCOPE_NAME = YES -GENERATE_TODOLIST = YES -GENERATE_TESTLIST = YES -GENERATE_BUGLIST = YES -GENERATE_DEPRECATEDLIST= YES -ENABLED_SECTIONS = -MAX_INITIALIZER_LINES = 30 -SHOW_USED_FILES = YES -SHOW_DIRECTORIES = NO -SHOW_FILES = YES -SHOW_NAMESPACES = YES -FILE_VERSION_FILTER = -LAYOUT_FILE = -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- -QUIET = NO -WARNINGS = YES -WARN_IF_UNDOCUMENTED = YES -WARN_IF_DOC_ERROR = YES -WARN_NO_PARAMDOC = NO -WARN_FORMAT = "$file:$line: $text" -WARN_LOGFILE = -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- -INPUT = ../doc/dox_cmd.h -INPUT_ENCODING = UTF-8 -FILE_PATTERNS = *.c \ - *.cc \ - *.cxx \ - *.cpp \ - *.c++ \ - *.d \ - *.java \ - *.ii \ - *.ixx \ - *.ipp \ - *.i++ \ - *.inl \ - *.h \ - *.hh \ - *.hxx \ - *.hpp \ - *.h++ \ - *.idl \ - *.odl \ - *.cs \ - *.php \ - *.php3 \ - *.inc \ - *.m \ - *.mm \ - *.dox \ - *.py -RECURSIVE = YES -EXCLUDE = -EXCLUDE_SYMLINKS = NO -EXCLUDE_PATTERNS = */.svn/* \ - */.svn -EXCLUDE_SYMBOLS = -EXAMPLE_PATH = -EXAMPLE_PATTERNS = * -EXAMPLE_RECURSIVE = NO -IMAGE_PATH = -INPUT_FILTER = -FILTER_PATTERNS = -FILTER_SOURCE_FILES = NO -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- -SOURCE_BROWSER = NO -INLINE_SOURCES = NO -STRIP_CODE_COMMENTS = YES -REFERENCED_BY_RELATION = NO -REFERENCES_RELATION = NO -REFERENCES_LINK_SOURCE = YES -USE_HTAGS = NO -VERBATIM_HEADERS = NO -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- -ALPHABETICAL_INDEX = NO -COLS_IN_ALPHA_INDEX = 5 -IGNORE_PREFIX = -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- -GENERATE_HTML = YES -HTML_OUTPUT = AssimpCmdDoc_Html -HTML_FILE_EXTENSION = .html -HTML_HEADER = -HTML_FOOTER = -HTML_STYLESHEET = style.css -HTML_ALIGN_MEMBERS = YES -HTML_DYNAMIC_SECTIONS = NO -GENERATE_DOCSET = NO -DOCSET_FEEDNAME = "Doxygen generated docs" -DOCSET_BUNDLE_ID = org.doxygen.Project -GENERATE_HTMLHELP = YES -CHM_FILE = AssimpCmdDoc.chm -HHC_LOCATION = "C:/Program Files (x86)/HTML Help Workshop/hhc.exe" -GENERATE_CHI = NO -CHM_INDEX_ENCODING = -BINARY_TOC = NO -TOC_EXPAND = NO -GENERATE_QHP = NO -QCH_FILE = -QHP_NAMESPACE = -QHP_VIRTUAL_FOLDER = doc -QHP_CUST_FILTER_NAME = -QHP_CUST_FILTER_ATTRS = -QHP_SECT_FILTER_ATTRS = -QHG_LOCATION = -DISABLE_INDEX = NO -ENUM_VALUES_PER_LINE = 4 -GENERATE_TREEVIEW = NO -TREEVIEW_WIDTH = 250 -FORMULA_FONTSIZE = 10 -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- -GENERATE_LATEX = NO -LATEX_OUTPUT = latex -LATEX_CMD_NAME = latex -MAKEINDEX_CMD_NAME = makeindex -COMPACT_LATEX = NO -PAPER_TYPE = a4wide -EXTRA_PACKAGES = -LATEX_HEADER = -PDF_HYPERLINKS = NO -USE_PDFLATEX = NO -LATEX_BATCHMODE = NO -LATEX_HIDE_INDICES = NO -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- -GENERATE_RTF = NO -RTF_OUTPUT = rtf -COMPACT_RTF = NO -RTF_HYPERLINKS = NO -RTF_STYLESHEET_FILE = -RTF_EXTENSIONS_FILE = -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- -GENERATE_MAN = NO -MAN_OUTPUT = man -MAN_EXTENSION = .3 -MAN_LINKS = NO -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- -GENERATE_XML = NO -XML_OUTPUT = xml -XML_SCHEMA = -XML_DTD = -XML_PROGRAMLISTING = YES -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- -GENERATE_AUTOGEN_DEF = NO -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- -GENERATE_PERLMOD = NO -PERLMOD_LATEX = NO -PERLMOD_PRETTY = YES -PERLMOD_MAKEVAR_PREFIX = -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- -ENABLE_PREPROCESSING = YES -MACRO_EXPANSION = YES -EXPAND_ONLY_PREDEF = YES -SEARCH_INCLUDES = YES -INCLUDE_PATH = -INCLUDE_FILE_PATTERNS = -PREDEFINED = ASSIMP_DOXYGEN_BUILD=1 \ - __cplusplus -EXPAND_AS_DEFINED = C_STRUCT \ - C_ENUM -SKIP_FUNCTION_MACROS = YES -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- -TAGFILES = -GENERATE_TAGFILE = -ALLEXTERNALS = NO -EXTERNAL_GROUPS = YES -PERL_PATH = /usr/bin/perl -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- -CLASS_DIAGRAMS = NO -MSCGEN_PATH = -HIDE_UNDOC_RELATIONS = YES -HAVE_DOT = NO -DOT_FONTNAME = FreeSans -DOT_FONTSIZE = 10 -DOT_FONTPATH = -CLASS_GRAPH = YES -COLLABORATION_GRAPH = YES -GROUP_GRAPHS = YES -UML_LOOK = NO -TEMPLATE_RELATIONS = NO -INCLUDE_GRAPH = YES -INCLUDED_BY_GRAPH = YES -CALL_GRAPH = NO -CALLER_GRAPH = NO -GRAPHICAL_HIERARCHY = YES -DIRECTORY_GRAPH = YES -DOT_IMAGE_FORMAT = png -DOT_PATH = -DOTFILE_DIRS = -DOT_GRAPH_MAX_NODES = 50 -MAX_DOT_GRAPH_DEPTH = 1000 -DOT_TRANSPARENT = NO -DOT_MULTI_TARGETS = NO -GENERATE_LEGEND = YES -DOT_CLEANUP = YES -#--------------------------------------------------------------------------- -# Options related to the search engine -#--------------------------------------------------------------------------- -SEARCHENGINE = NO +# Doxyfile 1.8.7 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = Tools + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = "v3.1.1 (June 2014)" + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is included in +# the documentation. The maximum height of the logo should not exceed 55 pixels +# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo +# to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class " \ + "The $name widget " \ + "The $name file " \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = NO + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a +# new page for each member. If set to NO, the documentation of a member will be +# part of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = YES + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = YES + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO these classes will be included in the various overviews. This option has +# no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = NO + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = YES + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = YES + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the +# todo list. This list is created by putting \todo commands in the +# documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the +# test list. This list is created by putting \test commands in the +# documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES the list +# will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. Do not use file names with spaces, bibtex cannot handle them. See +# also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO doxygen will only warn about wrong or incomplete parameter +# documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. + +INPUT = ../doc/dox_cmd.h + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox \ + *.py + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = */.svn/* \ + */.svn + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER ) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = NO + +# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# compiled with the --with-libclang option. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = NO + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = AssimpCmdDoc_Html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = style.css + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- +# defined cascading style sheet that is included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefor more robust against future updates. +# Doxygen will copy the style sheet file to the output directory. For an example +# see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the stylesheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = YES + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = AssimpCmdDoc.chm + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = "C:/Program Files (x86)/HTML Help Workshop/hhc.exe" + +# The GENERATE_CHI flag controls if a separate .chi index file is generated ( +# YES) or that it should be included in the master .chm file ( NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated ( +# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /