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 }