1 module des.assimp.loader; 2 3 import derelict.assimp3.assimp; 4 import derelict.assimp3.types; 5 6 import des.util.helpers; 7 import des.util.arch; 8 import des.util.data.type; 9 import des.util.stdext..string; 10 11 import des.assimp.mesh; 12 13 /// 14 class SMLoaderException : Exception 15 { 16 /// 17 this( string msg, string file=__FILE__, size_t line=__LINE__ ) pure nothrow @safe 18 { super( msg, file, line ); } 19 } 20 21 /// 22 class SMLoader : DesObject, SMMeshGenerator 23 { 24 mixin DES; 25 protected: 26 27 string scene_file_name; 28 const(aiScene)* scene; 29 30 string sourceName() const @property 31 { return "scene '" ~ scene_file_name ~ "'"; } 32 33 public: 34 35 /// process scene before loading, use Assimp3 documentation for more information 36 enum PostProcess 37 { 38 /// Calculates the tangents and bitangents for the imported meshes. 39 CalcTangentSpace = aiProcess_CalcTangentSpace, 40 41 /// Identifies and joins identical vertex data sets within all imported meshes. 42 JoinIdenticalVertices = aiProcess_JoinIdenticalVertices, 43 44 /// Converts all the imported data to a left-handed coordinate space. 45 MakeLeftHanded = aiProcess_MakeLeftHanded, 46 47 /// Triangulates all faces of all meshes. 48 Triangulate = aiProcess_Triangulate, 49 50 /++ Removes some parts of the data structure (animations, materials, 51 light sources, cameras, textures, vertex components). +/ 52 //RemoveComponent = aiProcess_RemoveComponent, 53 54 /// Generates normals for all faces of all meshes. 55 GenNormals = aiProcess_GenNormals, 56 57 /// Generates smooth normals for all vertices in the mesh. 58 GenSmoothNormals = aiProcess_GenSmoothNormals, 59 60 /// Splits large meshes into smaller sub-meshes. 61 SplitLargeMeshes = aiProcess_SplitLargeMeshes, 62 63 /++ <hr>Removes the node graph and pre-transforms all vertices with 64 the local transformation matrices of their nodes. +/ 65 PreTransformVertices = aiProcess_PreTransformVertices, 66 67 /// Limits the number of bones simultaneously affecting a single vertex to a maximum value. 68 LimitBoneWeights = aiProcess_LimitBoneWeights, 69 70 /// Validates the imported scene data structure. 71 ValidateDataStructure = aiProcess_ValidateDataStructure, 72 73 /// Reorders triangles for better vertex cache locality. 74 ImproveCacheLocality = aiProcess_ImproveCacheLocality, 75 76 /// Searches for redundant/unreferenced materials and removes them. 77 RemoveRedundantMaterials = aiProcess_RemoveRedundantMaterials, 78 79 /++ This step tries to determine which meshes have normal vectors 80 that are facing inwards and inverts them. +/ 81 FixInFacingNormals = aiProcess_FixInFacingNormals, 82 83 /++ This step splits meshes with more than one primitive type in 84 homogeneous sub-meshes. +/ 85 SortByPType = aiProcess_SortByPType, 86 87 /++ This step searches all meshes for degenerate primitives and 88 converts them to proper lines or points. +/ 89 FindDegenerates = aiProcess_FindDegenerates, 90 91 /++ This step searches all meshes for invalid data, such as zeroed 92 normal vectors or invalid UV coords and removes/fixes them. This is 93 intended to get rid of some common exporter errors. +/ 94 FindInvalidData = aiProcess_FindInvalidData, 95 96 /++ This step converts non-UV mappings (such as spherical or 97 cylindrical mapping) to proper texture coordinate channels. +/ 98 GenUVCoords = aiProcess_GenUVCoords, 99 100 /++ This step applies per-texture UV transformations and bakes 101 them into stand-alone vtexture coordinate channels. +/ 102 TransformUVCoords = aiProcess_TransformUVCoords, 103 104 /++ This step searches for duplicate meshes and replaces them 105 with references to the first mesh. +/ 106 FindInstances = aiProcess_FindInstances, 107 108 /// A postprocessing step to reduce the number of meshes. 109 OptimizeMeshes = aiProcess_OptimizeMeshes, 110 111 /// A postprocessing step to optimize the scene hierarchy. 112 OptimizeGraph = aiProcess_OptimizeGraph, 113 114 /++ This step flips all UV coordinates along the y-axis and adjusts 115 material settings and bitangents accordingly. +/ 116 FlipUVs = aiProcess_FlipUVs, 117 118 /// This step adjusts the output face winding order to be CW. 119 FlipWindingOrder = aiProcess_FlipWindingOrder, 120 121 /++ This step splits meshes with many bones into sub-meshes so that each 122 su-bmesh has fewer or as many bones as a given limit. +/ 123 SplitByBoneCount = aiProcess_SplitByBoneCount, 124 125 /// This step removes bones losslessly or according to some threshold. 126 Debone = aiProcess_Debone, 127 128 // aiProcess_GenEntityMeshes = 0x100000, 129 // aiProcess_OptimizeAnimations = 0x200000 130 // aiProcess_FixTexturePaths = 0x200000 131 }; 132 133 /// 134 this() 135 { 136 if( !DerelictASSIMP3.isLoaded ) 137 DerelictASSIMP3.load(); 138 } 139 140 /// 141 PostProcess[] default_post_process = [ PostProcess.OptimizeMeshes, 142 PostProcess.CalcTangentSpace, 143 PostProcess.JoinIdenticalVertices, 144 PostProcess.Triangulate ]; 145 146 /// 147 void loadScene( string fname, PostProcess[] pp... ) 148 { 149 scene_file_name = fname; 150 scene = aiImportFile( fname.toStringz, 151 packBitMask( default_post_process ~ pp ) ); 152 } 153 154 /// 155 SMMesh getMesh( string name ) 156 { 157 foreach( i; 0 .. scene.mNumMeshes ) 158 if( toDStringFix( scene.mMeshes[i].mName.data ) == name ) 159 return convMesh( scene.mMeshes[i] ); 160 throw new SMLoaderException( "no mesh '" ~ name ~ 161 "' in " ~ sourceName ); 162 } 163 164 /// 165 SMMesh getMesh( size_t no ) 166 { 167 if( no < scene.mNumMeshes ) 168 return convMesh( scene.mMeshes[no] ); 169 throw new SMLoaderException( "no mesh #" ~ to!string(no) ~ 170 " in " ~ sourceName ); 171 } 172 173 /// 174 SMMesh[] getAllMeshes() 175 { 176 SMMesh[] ret; 177 foreach( i; 0 .. scene.mNumMeshes ) 178 ret ~= convMesh( scene.mMeshes[i] ); 179 return ret; 180 } 181 182 protected: 183 184 SMMesh convMesh( in aiMesh* m ) 185 in{ assert( m !is null ); } body 186 { 187 return SMMesh 188 ( 189 SMMesh.Type.TRIANGLES, 190 toDStringFix( m.mName.data ), 191 getIndices( m ), 192 getVertices( m ), 193 getTexCoords( m ), 194 getNormals( m ), 195 getTangents( m ), 196 getBitangents( m ), 197 getColors( m ) 198 ); 199 } 200 201 uint[] getIndices( in aiMesh* m ) 202 { 203 uint[] ret; 204 205 foreach( i; 0 .. m.mNumFaces ) 206 { 207 auto f = m.mFaces[i]; 208 enforce( f.mNumIndices == 3, new SMLoaderException( "one or more faces is not triangle" ) ); 209 ret ~= getTypedArray!uint( 3, f.mIndices ).arr; 210 } 211 212 return ret; 213 } 214 215 vec3[] getVertices( in aiMesh* m ) 216 { return getVertVectors( m, m.mVertices ); } 217 218 vec3[] getNormals( in aiMesh* m ) 219 { return getVertVectors( m, m.mNormals ); } 220 221 vec3[] getTangents( in aiMesh* m ) 222 { return getVertVectors( m, m.mTangents ); } 223 224 vec3[] getBitangents( in aiMesh* m ) 225 { return getVertVectors( m, m.mBitangents ); } 226 227 vec3[] getVertVectors( in aiMesh* m, in aiVector3D* buf ) 228 { 229 if( buf is null ) return null; 230 return getTypedArray!vec3( m.mNumVertices, buf ).arr.dup; 231 } 232 233 SMTexCoord[] getTexCoords( in aiMesh* m ) 234 { 235 SMTexCoord[] ret; 236 foreach( t; 0 .. AI_MAX_NUMBER_OF_TEXTURECOORDS ) 237 { 238 auto tc = m.mTextureCoords[t]; 239 if( tc is null ) continue; 240 auto nvert = m.mNumVertices; 241 auto comp = m.mNumUVComponents[t]; 242 auto buf = new float[]( comp * nvert ); 243 foreach( i; 0 .. nvert ) 244 foreach( j; 0 .. comp ) 245 buf[i*comp+j] = *(cast(float*)( tc + i ) + j); 246 ret ~= SMTexCoord( comp, buf ); 247 } 248 return ret; 249 } 250 251 vec4[][] getColors( in aiMesh* m ) 252 { 253 vec4[][] ret; 254 255 foreach( i; 0 .. AI_MAX_NUMBER_OF_COLOR_SETS ) 256 { 257 if( m.mColors[i] is null ) continue; 258 ret ~= getTypedArray!vec4( m.mNumVertices, m.mColors[i] ).arr.dup; 259 } 260 261 return ret; 262 } 263 }