1 /* 2 Open Asset Import Library (assimp) 3 ---------------------------------------------------------------------- 4 5 Copyright (c) 2006-2021, assimp team 6 7 All rights reserved. 8 9 Redistribution and use of this software in source and binary forms, 10 with or without modification, are permitted provided that the 11 following conditions are met: 12 13 * Redistributions of source code must retain the above 14 copyright notice, this list of conditions and the 15 following disclaimer. 16 17 * Redistributions in binary form must reproduce the above 18 copyright notice, this list of conditions and the 19 following disclaimer in the documentation and/or other 20 materials provided with the distribution. 21 22 * Neither the name of the assimp team, nor the names of its 23 contributors may be used to endorse or promote products 24 derived from this software without specific prior 25 written permission of the assimp team. 26 27 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 28 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 29 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 30 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 31 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 32 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 33 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 34 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 35 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 36 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 37 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 38 39 ---------------------------------------------------------------------- 40 */ 41 42 /// @file Definition of the base class for all importer worker classes. 43 44 #pragma once 45 #ifndef INCLUDED_AI_BASEIMPORTER_H 46 #define INCLUDED_AI_BASEIMPORTER_H 47 48 #ifdef __GNUC__ 49 #pragma GCC system_header 50 #endif 51 52 #include "Exceptional.h" 53 54 #include <assimp/types.h> 55 #include <assimp/ProgressHandler.hpp> 56 #include <set> 57 #include <vector> 58 #include <memory> 59 60 struct aiScene; 61 struct aiImporterDesc; 62 63 namespace Assimp { 64 65 class Importer; 66 class IOSystem; 67 class BaseProcess; 68 class SharedPostProcessInfo; 69 class IOStream; 70 71 // utility to do char4 to uint32 in a portable manner 72 #define AI_MAKE_MAGIC(string) ((uint32_t)((string[0] << 24) + \ 73 (string[1] << 16) + (string[2] << 8) + string[3])) 74 75 // --------------------------------------------------------------------------- 76 /** FOR IMPORTER PLUGINS ONLY: The BaseImporter defines a common interface 77 * for all importer worker classes. 78 * 79 * The interface defines two functions: CanRead() is used to check if the 80 * importer can handle the format of the given file. If an implementation of 81 * this function returns true, the importer then calls ReadFile() which 82 * imports the given file. ReadFile is not overridable, it just calls 83 * InternReadFile() and catches any ImportErrorException that might occur. 84 */ 85 class ASSIMP_API BaseImporter { 86 friend class Importer; 87 88 public: 89 /** Constructor to be privately used by #Importer */ 90 BaseImporter() AI_NO_EXCEPT; 91 92 /** Destructor, private as well */ 93 virtual ~BaseImporter(); 94 95 // ------------------------------------------------------------------- 96 /** Returns whether the class can handle the format of the given file. 97 * 98 * The implementation should be as quick as possible. A check for 99 * the file extension is enough. If no suitable loader is found with 100 * this strategy, CanRead() is called again, the 'checkSig' parameter 101 * set to true this time. Now the implementation is expected to 102 * perform a full check of the file structure, possibly searching the 103 * first bytes of the file for magic identifiers or keywords. 104 * 105 * @param pFile Path and file name of the file to be examined. 106 * @param pIOHandler The IO handler to use for accessing any file. 107 * @param checkSig Set to true if this method is called a second time. 108 * This time, the implementation may take more time to examine the 109 * contents of the file to be loaded for magic bytes, keywords, etc 110 * to be able to load files with unknown/not existent file extensions. 111 * @return true if the class can read this file, false if not. 112 */ 113 virtual bool CanRead( 114 const std::string &pFile, 115 IOSystem *pIOHandler, 116 bool checkSig) const = 0; 117 118 // ------------------------------------------------------------------- 119 /** Imports the given file and returns the imported data. 120 * If the import succeeds, ownership of the data is transferred to 121 * the caller. If the import fails, nullptr is returned. The function 122 * takes care that any partially constructed data is destroyed 123 * beforehand. 124 * 125 * @param pImp #Importer object hosting this loader. 126 * @param pFile Path of the file to be imported. 127 * @param pIOHandler IO-Handler used to open this and possible other files. 128 * @return The imported data or nullptr if failed. If it failed a 129 * human-readable error description can be retrieved by calling 130 * GetErrorText() 131 * 132 * @note This function is not intended to be overridden. Implement 133 * InternReadFile() to do the import. If an exception is thrown somewhere 134 * in InternReadFile(), this function will catch it and transform it into 135 * a suitable response to the caller. 136 */ 137 aiScene *ReadFile( 138 Importer *pImp, 139 const std::string &pFile, 140 IOSystem *pIOHandler); 141 142 // ------------------------------------------------------------------- 143 /** Returns the error description of the last error that occurred. 144 * If the error is due to a std::exception, this will return the message. 145 * Exceptions can also be accessed with GetException(). 146 * @return A description of the last error that occurred. An empty 147 * string if there was no error. 148 */ GetErrorText()149 const std::string &GetErrorText() const { 150 return m_ErrorText; 151 } 152 153 // ------------------------------------------------------------------- 154 /** Returns the exception of the last exception that occurred. 155 * Note: Exceptions are not the only source of error details, so GetErrorText 156 * should be consulted too. 157 * @return The last exception that occurred. 158 */ GetException()159 const std::exception_ptr& GetException() const { 160 return m_Exception; 161 } 162 163 // ------------------------------------------------------------------- 164 /** Called prior to ReadFile(). 165 * The function is a request to the importer to update its configuration 166 * basing on the Importer's configuration property list. 167 * @param pImp Importer instance 168 */ 169 virtual void SetupProperties( 170 const Importer *pImp); 171 172 // ------------------------------------------------------------------- 173 /** Called by #Importer::GetImporterInfo to get a description of 174 * some loader features. Importers must provide this information. */ 175 virtual const aiImporterDesc *GetInfo() const = 0; 176 177 /** 178 * Will be called only by scale process when scaling is requested. 179 */ SetFileScale(double scale)180 void SetFileScale(double scale) { 181 fileScale = scale; 182 } 183 184 // ------------------------------------------------------------------- 185 /** Called by #Importer::GetExtensionList for each loaded importer. 186 * Take the extension list contained in the structure returned by 187 * #GetInfo and insert all file extensions into the given set. 188 * @param extension set to collect file extensions in*/ 189 void GetExtensionList(std::set<std::string> &extensions); 190 191 protected: 192 double importerScale = 1.0; 193 double fileScale = 1.0; 194 195 // ------------------------------------------------------------------- 196 /** Imports the given file into the given scene structure. The 197 * function is expected to throw an ImportErrorException if there is 198 * an error. If it terminates normally, the data in aiScene is 199 * expected to be correct. Override this function to implement the 200 * actual importing. 201 * <br> 202 * The output scene must meet the following requirements:<br> 203 * <ul> 204 * <li>At least a root node must be there, even if its only purpose 205 * is to reference one mesh.</li> 206 * <li>aiMesh::mPrimitiveTypes may be 0. The types of primitives 207 * in the mesh are determined automatically in this case.</li> 208 * <li>the vertex data is stored in a pseudo-indexed "verbose" format. 209 * In fact this means that every vertex that is referenced by 210 * a face is unique. Or the other way round: a vertex index may 211 * not occur twice in a single aiMesh.</li> 212 * <li>aiAnimation::mDuration may be -1. Assimp determines the length 213 * of the animation automatically in this case as the length of 214 * the longest animation channel.</li> 215 * <li>aiMesh::mBitangents may be nullptr if tangents and normals are 216 * given. In this case bitangents are computed as the cross product 217 * between normal and tangent.</li> 218 * <li>There needn't be a material. If none is there a default material 219 * is generated. However, it is recommended practice for loaders 220 * to generate a default material for yourself that matches the 221 * default material setting for the file format better than Assimp's 222 * generic default material. Note that default materials *should* 223 * be named AI_DEFAULT_MATERIAL_NAME if they're just color-shaded 224 * or AI_DEFAULT_TEXTURED_MATERIAL_NAME if they define a (dummy) 225 * texture. </li> 226 * </ul> 227 * If the AI_SCENE_FLAGS_INCOMPLETE-Flag is <b>not</b> set:<ul> 228 * <li> at least one mesh must be there</li> 229 * <li> there may be no meshes with 0 vertices or faces</li> 230 * </ul> 231 * This won't be checked (except by the validation step): Assimp will 232 * crash if one of the conditions is not met! 233 * 234 * @param pFile Path of the file to be imported. 235 * @param pScene The scene object to hold the imported data. 236 * nullptr is not a valid parameter. 237 * @param pIOHandler The IO handler to use for any file access. 238 * nullptr is not a valid parameter. */ 239 virtual void InternReadFile( 240 const std::string &pFile, 241 aiScene *pScene, 242 IOSystem *pIOHandler) = 0; 243 244 public: // static utilities 245 // ------------------------------------------------------------------- 246 /** A utility for CanRead(). 247 * 248 * The function searches the header of a file for a specific token 249 * and returns true if this token is found. This works for text 250 * files only. There is a rudimentary handling of UNICODE files. 251 * The comparison is case independent. 252 * 253 * @param pIOSystem IO System to work with 254 * @param file File name of the file 255 * @param tokens List of tokens to search for 256 * @param numTokens Size of the token array 257 * @param searchBytes Number of bytes to be searched for the tokens. 258 */ 259 static bool SearchFileHeaderForToken( 260 IOSystem *pIOSystem, 261 const std::string &file, 262 const char * const *tokens, 263 unsigned int numTokens, 264 unsigned int searchBytes = 200, 265 bool tokensSol = false, 266 bool noAlphaBeforeTokens = false); 267 268 // ------------------------------------------------------------------- 269 /** @brief Check whether a file has a specific file extension 270 * @param pFile Input file 271 * @param ext0 Extension to check for. Lowercase characters only, no dot! 272 * @param ext1 Optional second extension 273 * @param ext2 Optional third extension 274 * @note Case-insensitive 275 */ 276 static bool SimpleExtensionCheck( 277 const std::string &pFile, 278 const char *ext0, 279 const char *ext1 = nullptr, 280 const char *ext2 = nullptr); 281 282 // ------------------------------------------------------------------- 283 /** @brief Extract file extension from a string 284 * @param pFile Input file 285 * @return Extension without trailing dot, all lowercase 286 */ 287 static std::string GetExtension( 288 const std::string &pFile); 289 290 // ------------------------------------------------------------------- 291 /** @brief Check whether a file starts with one or more magic tokens 292 * @param pFile Input file 293 * @param pIOHandler IO system to be used 294 * @param magic n magic tokens 295 * @params num Size of magic 296 * @param offset Offset from file start where tokens are located 297 * @param Size of one token, in bytes. Maximally 16 bytes. 298 * @return true if one of the given tokens was found 299 * 300 * @note For convenience, the check is also performed for the 301 * byte-swapped variant of all tokens (big endian). Only for 302 * tokens of size 2,4. 303 */ 304 static bool CheckMagicToken( 305 IOSystem *pIOHandler, 306 const std::string &pFile, 307 const void *magic, 308 unsigned int num, 309 unsigned int offset = 0, 310 unsigned int size = 4); 311 312 // ------------------------------------------------------------------- 313 /** An utility for all text file loaders. It converts a file to our 314 * UTF8 character set. Errors are reported, but ignored. 315 * 316 * @param data File buffer to be converted to UTF8 data. The buffer 317 * is resized as appropriate. */ 318 static void ConvertToUTF8( 319 std::vector<char> &data); 320 321 // ------------------------------------------------------------------- 322 /** An utility for all text file loaders. It converts a file from our 323 * UTF8 character set back to ISO-8859-1. Errors are reported, but ignored. 324 * 325 * @param data File buffer to be converted from UTF8 to ISO-8859-1. The buffer 326 * is resized as appropriate. */ 327 static void ConvertUTF8toISO8859_1( 328 std::string &data); 329 330 // ------------------------------------------------------------------- 331 /// @brief Enum to define, if empty files are ok or not. 332 enum TextFileMode { 333 ALLOW_EMPTY, 334 FORBID_EMPTY 335 }; 336 337 // ------------------------------------------------------------------- 338 /** Utility for text file loaders which copies the contents of the 339 * file into a memory buffer and converts it to our UTF8 340 * representation. 341 * @param stream Stream to read from. 342 * @param data Output buffer to be resized and filled with the 343 * converted text file data. The buffer is terminated with 344 * a binary 0. 345 * @param mode Whether it is OK to load empty text files. */ 346 static void TextFileToBuffer( 347 IOStream *stream, 348 std::vector<char> &data, 349 TextFileMode mode = FORBID_EMPTY); 350 351 // ------------------------------------------------------------------- 352 /** Utility function to move a std::vector into a aiScene array 353 * @param vec The vector to be moved 354 * @param out The output pointer to the allocated array. 355 * @param numOut The output count of elements copied. */ 356 template <typename T> CopyVector(std::vector<T> & vec,T * & out,unsigned int & outLength)357 AI_FORCE_INLINE static void CopyVector( 358 std::vector<T> &vec, 359 T *&out, 360 unsigned int &outLength) { 361 outLength = unsigned(vec.size()); 362 if (outLength) { 363 out = new T[outLength]; 364 std::swap_ranges(vec.begin(), vec.end(), out); 365 } 366 } 367 368 // ------------------------------------------------------------------- 369 /** Utility function to move a std::vector of unique_ptrs into a aiScene array 370 * @param vec The vector of unique_ptrs to be moved 371 * @param out The output pointer to the allocated array. 372 * @param numOut The output count of elements copied. */ 373 template <typename T> CopyVector(std::vector<std::unique_ptr<T>> & vec,T ** & out,unsigned int & outLength)374 AI_FORCE_INLINE static void CopyVector( 375 std::vector<std::unique_ptr<T> > &vec, 376 T **&out, 377 unsigned int &outLength) { 378 outLength = unsigned(vec.size()); 379 if (outLength) { 380 out = new T*[outLength]; 381 T** outPtr = out; 382 std::for_each(vec.begin(), vec.end(), [&outPtr](std::unique_ptr<T>& uPtr){*outPtr = uPtr.release(); ++outPtr; }); 383 } 384 } 385 386 private: 387 /* Pushes state into importer for the importer scale */ 388 void UpdateImporterScale(Importer *pImp); 389 390 protected: 391 /// Error description in case there was one. 392 std::string m_ErrorText; 393 /// The exception, in case there was one. 394 std::exception_ptr m_Exception; 395 /// Currently set progress handler. 396 ProgressHandler *m_progress; 397 }; 398 399 } // end of namespace Assimp 400 401 #endif // AI_BASEIMPORTER_H_INC 402