1 module armos.graphics.model; 2 3 import armos.graphics; 4 import armos.math; 5 import armos.types; 6 7 /++ 8 3Dモデルを読み込み,描画するclassです. 9 +/ 10 class Model { 11 public{ 12 Mesh[] meshes; 13 Material[] materials; 14 Entity[] entities; 15 16 /++ 17 モデルを読み込みます. 18 19 読み込み時にmeshはmaterial毎に分割されます. 20 +/ 21 Model load(in string pathInDataDir){ 22 auto loadedModel = (new AssimpModelLoader).load(pathInDataDir); 23 meshes = loadedModel.meshes; 24 materials = loadedModel.materials; 25 entities = loadedModel.entities; 26 return this; 27 } 28 29 /++ 30 読み込まれたmeshの数を返します. 31 +/ 32 size_t numMeshes()const{ 33 return meshes.length; 34 } 35 36 /++ 37 読み込まれたmaterialの数を返します. 38 +/ 39 size_t numMaterials()const{ 40 return materials.length; 41 } 42 43 /++ 44 modelを描画します. 45 Params: 46 renderMode = 面,線,点のどれを描画するか指定します. 47 +/ 48 Model draw(in PolyRenderMode renderMode){ 49 foreach (entity; entities) { 50 entity.draw(renderMode); 51 } 52 return this; 53 }; 54 55 /++ 56 modelをワイヤフレームで描画します. 57 +/ 58 Model drawWireFrame(){ 59 draw(PolyRenderMode.WireFrame); 60 return this; 61 }; 62 63 /++ 64 modelの頂点を点で描画します. 65 +/ 66 Model drawVertices(){ 67 draw(PolyRenderMode.Points); 68 return this; 69 }; 70 71 /++ 72 meshの面を塗りつぶして描画します. 73 +/ 74 Model drawFill(){ 75 draw(PolyRenderMode.Fill); 76 return this; 77 }; 78 79 }//public 80 81 private{ 82 }//private 83 }//class Model 84 85 import derelict.assimp3.assimp; 86 import std.stdio; 87 import std.algorithm : map; 88 import std.array : array, Appender; 89 90 /++ 91 Assimpによりmodelの読み込みを行います. 92 +/ 93 class AssimpModelLoader { 94 public{ 95 ~this(){ 96 clear; 97 } 98 99 /// modelを読み込みそれを返します. 100 Model load(in string pathInDataDir){ 101 import std.string; 102 import armos.utils; 103 _modelfilepath = absolutePath(pathInDataDir); 104 105 import derelict.assimp3.assimp; 106 import std.stdio; 107 DerelictASSIMP3.load(); 108 109 loadScene(_modelfilepath); 110 111 return createdModel(_scene); 112 } 113 114 /// 読み込んだモデルを削除します. 115 void clear(){ 116 if(_isLoaded){ 117 aiReleaseImport(_scene); 118 _isLoaded = false; 119 } 120 } 121 122 123 }//public 124 125 private{ 126 127 /// 128 void loadScene(in string fileName){ 129 auto f = File(fileName, "r"); 130 131 char[] str; 132 while (f.readln(str)){ 133 // write(str); 134 } 135 136 aiPropertyStore* store = aiCreatePropertyStore(); 137 immutable uint flags = aiProcess_CalcTangentSpace 138 | aiProcess_Triangulate 139 | aiProcess_JoinIdenticalVertices 140 | aiProcess_SortByPType; 141 142 import std.string; 143 _scene = cast(aiScene*)aiImportFile(fileName.toStringz, flags); 144 // _scene = cast(aiScene*)aiImportFileExWithProperties(str.ptr , flags, null, store); 145 146 if(_scene){ 147 _isLoaded = true; 148 } 149 } 150 151 /// 152 static string fromAiString(const(aiString) s) @safe pure { 153 return s.data[0 .. s.length].idup; 154 } 155 156 /// 157 static armos.types.Color fromAiColor(ref const(aiColor4D) c){ 158 return armos.types.Color(c.r*armos.types.Color.limit, c.g*armos.types.Color.limit, c.b*armos.types.Color.limit, c.a*armos.types.Color.limit); 159 } 160 161 /// 162 static Vector3f fromAiVector3f(ref const( aiVector3D ) v){ 163 return Vector3f(v.x, v.y, v.z); 164 } 165 166 static Vector2f fromAiVector2f(ref const aiVector2D v){ 167 return Vector2f(v.x, v.y); 168 } 169 170 Model createdModel(const(aiScene)* scene)const{ 171 import std.stdio; 172 auto t = _scene.mRootNode.mTransformation; 173 Matrix4f rootTransformation = Matrix4f([ 174 [t.a1, t.a2, t.a3, t.a4], 175 [t.b1, t.b2, t.b3, t.b4], 176 [t.c1, t.c2, t.c3, t.c4], 177 [t.d1, t.d2, t.d3, t.d4], 178 ]); 179 180 import std.range; 181 auto model = new Model; 182 model.materials = scene.mMaterials[0 .. scene.mNumMaterials] 183 .map!(m => createdMaterial(m)) 184 .array; 185 186 model.meshes = scene.mMeshes[0 .. scene.mNumMeshes] 187 .map!(m => createdMesh(m, rootTransformation)) 188 .array; 189 190 model.entities = scene.mMeshes[0 .. scene.mNumMeshes] 191 .length 192 .iota 193 .map!( 194 i => (new Entity).mesh(model.meshes[i]) 195 .material(model.materials[scene.mMeshes[i].mMaterialIndex]) 196 ) 197 .array; 198 return model; 199 } 200 201 Material createdMaterial(const(aiMaterial)* material) const { 202 aiString aiName; 203 aiGetMaterialString(material, AI_MATKEY_NAME, 0, 0, &aiName); 204 auto name = fromAiString(aiName); 205 206 207 Material mat; 208 209 //texture 210 aiString aiPath; 211 if( 212 aiGetMaterialTexture( 213 material, 214 aiTextureType_DIFFUSE, 0, &aiPath, 215 null, null, null, null, null 216 ) == aiReturn_SUCCESS 217 ){ 218 auto image = new Image; 219 220 import std.path; 221 immutable string textureFileName = fromAiString( aiPath ); 222 image.load(buildPath( dirName(_modelfilepath), textureFileName )); 223 224 mat = new DefaultMaterial; 225 mat.texture("tex0", image.texture); 226 }else{ 227 mat = new NoTextureMaterial; 228 } 229 230 aiColor4D color; 231 232 //diffuse 233 aiGetMaterialColor(material, AI_MATKEY_COLOR_DIFFUSE, 0, 0, &color); 234 mat.uniform("diffuse", fromAiColor(color)); 235 236 //speculer 237 aiGetMaterialColor(material, AI_MATKEY_COLOR_SPECULAR, 0, 0, &color); 238 mat.uniform("speculer", fromAiColor(color)); 239 240 //ambient 241 aiGetMaterialColor(material, AI_MATKEY_COLOR_AMBIENT, 0, 0, &color); 242 mat.uniform("ambient", fromAiColor(color)); 243 244 return mat; 245 } 246 247 Mesh createdMesh(const(aiMesh)* mesh, Matrix4f matrix) const { 248 // Mesh createMesh(const(aiMesh)* mesh, Material[] materials) const { 249 auto name = fromAiString(mesh.mName); 250 251 auto vertices = 252 mesh.mVertices[0 .. mesh.mNumVertices] 253 .map!(v => fromAiVector3f(v)) 254 .array; 255 foreach (ref v3; vertices) { 256 auto v4 = Vector4f(v3.x, v3.y, v3.z, 0); 257 v3 = (matrix*v4).xyz; 258 } 259 260 const(Vector3f)[] normals; 261 if(mesh.mNormals !is null) { 262 normals = mesh.mNormals[0 .. mesh.mNumVertices] 263 .map!(n => fromAiVector3f(n)) 264 .array; 265 } 266 267 auto convertedMesh= new Mesh; 268 269 if(mesh.mTextureCoords[0] !is null){ 270 convertedMesh.texCoords = new Vector4f[mesh.mNumVertices]; 271 for (int i = 0; i < mesh.mNumVertices; i++) { 272 convertedMesh.texCoords[i] = Vector4f(mesh.mTextureCoords[0][i].x, mesh.mTextureCoords[0][i].y, 0f, 1f); 273 } 274 } 275 276 convertedMesh.vertices = vertices.map!((Vector3f vec){ 277 return Vector4f(vec[0], vec[1], vec[2], 1f); 278 }).array; 279 280 auto totalIndices = 0; 281 foreach(f; mesh.mFaces[0 .. mesh.mNumFaces]) { 282 totalIndices += f.mNumIndices; 283 } 284 285 convertedMesh.indices = new int[totalIndices]; 286 auto indexCounter = 0; 287 foreach(f; mesh.mFaces[0 .. mesh.mNumFaces]) { 288 immutable int numVertices = f.mNumIndices; 289 foreach(i; f.mIndices[0 .. numVertices]){ 290 convertedMesh.indices[indexCounter] = i; 291 indexCounter++; 292 } 293 } 294 // immutable mi = mesh.mMaterialIndex; 295 // auto material = (mi < materials.length) ? materials[mi] : null; 296 // convertedMesh.material = material; 297 298 return convertedMesh; 299 } 300 301 aiScene* _scene; 302 bool _isLoaded = false; 303 string _modelfilepath; 304 }//private 305 }//class AssimpModelLoader 306 307 /// 308 class NoTextureMaterial : Material{ 309 mixin MaterialImpl; 310 311 /// 312 this(){ 313 _shader = new armos.graphics.Shader; 314 _shader.loadSources(noTextureVertexShaderSource, "", noTextureFragmentShaderSource); 315 } 316 }//class NoTextureMaterial 317 318 private immutable string noTextureVertexShaderSource = q{ 319 #version 330 320 321 uniform mat4 modelViewMatrix; 322 uniform mat4 projectionMatrix; 323 uniform mat4 modelViewProjectionMatrix; 324 325 in vec4 vertex; 326 in vec3 normal; 327 in vec3 tangent; 328 in vec4 color; 329 330 out vec4 f_color; 331 332 void main(void) { 333 gl_Position = modelViewProjectionMatrix * vertex; 334 f_color = color; 335 } 336 }; 337 338 private immutable string noTextureFragmentShaderSource = q{ 339 #version 330 340 341 in vec4 f_color; 342 343 out vec4 fragColor; 344 345 uniform vec4 diffuse; 346 347 void main(void) { 348 fragColor = diffuse; 349 } 350 };