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