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 }