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 /// Releases the ASSIMP library and all resources. 64 /// All resources should have been released at this point, 65 /// since you won't be able to call any ASSIMP function afterwards. 66 ~this() 67 { 68 if (_libInitialized) 69 { 70 debug ensureNotInGC("Assimp"); 71 aiDetachLogStream(&_logStream); 72 _libInitialized = false; 73 } 74 } 75 deprecated("Use .destroy instead") void close(){} 76 77 /// Returns: ASSIMP version string as returned by the dynamic library. 78 string getVersion() 79 { 80 string compileFlags() 81 { 82 string[] res; 83 uint flags = aiGetCompileFlags(); 84 if ((flags & ASSIMP_CFLAGS_SHARED) != 0) 85 res ~= "shared"; 86 if ((flags & ASSIMP_CFLAGS_STLPORT) != 0) 87 res ~= "stl-port"; 88 if ((flags & ASSIMP_CFLAGS_DEBUG) != 0) 89 res ~= "debug"; 90 if ((flags & ASSIMP_CFLAGS_NOBOOST) != 0) 91 res ~= "no-boost"; 92 if ((flags & ASSIMP_CFLAGS_SINGLETHREADED) != 0) 93 res ~= "single-threaded"; 94 return join(res, ", "); 95 } 96 return format("v%s.%s_r%s (%s)", aiGetVersionMajor(), aiGetVersionMinor(), aiGetVersionRevision(), compileFlags()); 97 } 98 99 /// Returns: A string with legal copyright and licensing information about Assimp. 100 const(char)[] getLegalString() 101 { 102 const(char)* legalZ = aiGetLegalString(); 103 return fromStringz(legalZ); 104 } 105 } 106 107 package 108 { 109 Logger _logger; 110 111 // exception mechanism that shall be used by every module here 112 void throwAssimpException(string callThatFailed) 113 { 114 string message = format("%s failed: %s", callThatFailed, getErrorString()); 115 throw new AssimpException(message); 116 } 117 118 const(char)[] getErrorString() 119 { 120 const(char)* errorZ = aiGetErrorString(); 121 return fromStringz(errorZ); 122 } 123 } 124 125 private 126 { 127 bool _libInitialized; 128 aiLogStream _logStream; 129 } 130 } 131 132 extern (C) private 133 { 134 void loggingCallbackAssimp(const(char)* message, char* user) nothrow 135 { 136 Assimp assimp = cast(Assimp)user; 137 try 138 { 139 Logger logger = assimp._logger; 140 logger.infof("assimp: %s"); 141 } 142 catch(Exception e) 143 { 144 // ignoring IO exceptions, format errors, etc... to be nothrow 145 // making the whole Log interface nothrow is not that trivial 146 } 147 } 148 } 149 150 /// Crash if the GC is running. 151 /// Useful in destructors to avoid reliance GC resource release. 152 package void ensureNotInGC(string resourceName) nothrow 153 { 154 import core.exception; 155 try 156 { 157 import core.memory; 158 cast(void) GC.malloc(1); // not ideal since it allocates 159 return; 160 } 161 catch(InvalidMemoryOperationError e) 162 { 163 164 import core.stdc.stdio; 165 fprintf(stderr, "Error: clean-up of %s incorrectly depends on destructors called by the GC.\n", resourceName.ptr); 166 assert(false); 167 } 168 }