1 module gfm.assimp.assimp; 2 3 import std.conv, 4 std.string, 5 std.array : join; 6 7 import std.logger; 8 9 import gfm.core.text; 10 11 import derelict.assimp3.assimp, 12 derelict.util.exception; 13 14 /// The one exception type thrown in this wrapper. 15 /// A failing ASSIMP function should <b>always</b> throw an AssimpException. 16 class AssimpException : Exception 17 { 18 public 19 { 20 this(string msg) 21 { 22 super(msg); 23 } 24 } 25 } 26 27 /// Create one to use the ASSIMP libary. 28 /// Owns both the loader and logging redirection. 29 /// This object is passed around to other ASSIMP wrapper objects 30 /// to ensure library loading. 31 final class Assimp 32 { 33 public 34 { 35 /// Load ASSIMP library, redirect logging to our logger. 36 /// You can pass a null logger if you don't want logging. 37 /// Throws: AssimpException on error. 38 this(Logger logger) 39 { 40 _logger = logger is null ? new NullLogger() : logger; 41 42 try 43 { 44 DerelictASSIMP3.load(); 45 } 46 catch(DerelictException e) 47 { 48 throw new AssimpException(e.msg); 49 } 50 51 _libInitialized = true; 52 53 _logger.infof("Assimp %s initialized.", getVersion()); 54 _logger.infof("%s.", getLegalString()); 55 56 // enable verbose logging in debug-mode 57 debug 58 aiEnableVerboseLogging(AI_TRUE); 59 else 60 aiEnableVerboseLogging(AI_FALSE); 61 62 // route Assimp logging to our own 63 _logStream.callback = &loggingCallbackAssimp; 64 _logStream.user = cast(char*)(cast(void*)this); 65 aiAttachLogStream(&_logStream); 66 } 67 68 ~this() 69 { 70 close(); 71 } 72 73 /// Releases the ASSIMP library and all resources. 74 /// All resources should have been released at this point, 75 /// since you won't be able to call any ASSIMP function afterwards. 76 void close() 77 { 78 if (_libInitialized) 79 { 80 aiDetachLogStream(&_logStream); 81 DerelictASSIMP3.unload(); 82 _libInitialized = false; 83 } 84 } 85 86 /// Returns: ASSIMP version string as returned by the dynamic library. 87 string getVersion() 88 { 89 string compileFlags() 90 { 91 string[] res; 92 uint flags = aiGetCompileFlags(); 93 if ((flags & ASSIMP_CFLAGS_SHARED) != 0) 94 res ~= "shared"; 95 if ((flags & ASSIMP_CFLAGS_STLPORT) != 0) 96 res ~= "stl-port"; 97 if ((flags & ASSIMP_CFLAGS_DEBUG) != 0) 98 res ~= "debug"; 99 if ((flags & ASSIMP_CFLAGS_NOBOOST) != 0) 100 res ~= "no-boost"; 101 if ((flags & ASSIMP_CFLAGS_SINGLETHREADED) != 0) 102 res ~= "single-threaded"; 103 return join(res, ", "); 104 } 105 return format("v%s.%s_r%s (%s)", aiGetVersionMajor(), aiGetVersionMinor(), aiGetVersionRevision(), compileFlags()); 106 } 107 108 /// Returns: A string with legal copyright and licensing information about Assimp. 109 string getLegalString() 110 { 111 const(char)* legalZ = aiGetLegalString(); 112 return sanitizeUTF8(legalZ, _logger, "Assimp legal string"); 113 } 114 } 115 116 package 117 { 118 Logger _logger; 119 120 // exception mechanism that shall be used by every module here 121 void throwAssimpException(string callThatFailed) 122 { 123 string message = format("%s failed: %s", callThatFailed, getErrorString()); 124 throw new AssimpException(message); 125 } 126 127 string getErrorString() 128 { 129 const(char)* errorZ = aiGetErrorString(); 130 return sanitizeUTF8(errorZ, _logger, "Assimp error string"); 131 } 132 } 133 134 private 135 { 136 bool _libInitialized; 137 aiLogStream _logStream; 138 } 139 } 140 141 extern (C) private 142 { 143 void loggingCallbackAssimp(const(char)* message, char* user) nothrow 144 { 145 Assimp assimp = cast(Assimp)user; 146 try 147 { 148 Logger logger = assimp._logger; 149 logger.infof("assimp: %s", sanitizeUTF8(message, log, "Assimp logging")); 150 } 151 catch(Exception e) 152 { 153 // ignoring IO exceptions, format errors, etc... to be nothrow 154 // making the whole Log interface nothrow is not that trivial 155 } 156 } 157 }