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