1 /**
2  * Retrograde Engine
3  *
4  * Authors:
5  *  Mike Bierlee, m.bierlee@lostmoment.com
6  * Copyright: 2014-2021 Mike Bierlee
7  * License:
8  *  This software is licensed under the terms of the MIT license.
9  *  The full terms of the license can be found in the LICENSE.txt file.
10  */
11 
12 module retrograde.pipeline.assimp;
13 
14 version(Have_derelict_assimp3) {
15 
16 import retrograde.file;
17 import retrograde.geometry;
18 import retrograde.math;
19 
20 import std.string;
21 import std.exception;
22 
23 import derelict.assimp3.assimp;
24 
25 class AssimpImportException : Exception {
26     mixin basicExceptionCtors;
27 }
28 
29 class AssimpSceneImporter {
30 
31     public const(aiScene*) importScene(File file) {
32         auto scene = aiImportFile(file.fileName.toStringz(), aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType | aiProcess_GenUVCoords | aiProcess_TransformUVCoords);
33 
34         if (!scene) {
35             auto errorMessage = cast(string) aiGetErrorString().fromStringz();
36             throw new AssimpImportException(errorMessage);
37         }
38 
39         return scene;
40     }
41 
42     public void releaseImport(const(aiScene*) scene) {
43         aiReleaseImport(scene);
44     }
45 
46 }
47 
48 version(Have_derelict_gl3) {
49 
50 import retrograde.graphics.threedee.opengl.model;
51 
52 class ModelCreationException : Exception {
53     mixin basicExceptionCtors;
54 }
55 
56 class AssimpOpenglModelFactory {
57 
58     public OpenGlModel createFromScene(const(aiScene*) scene) {
59         Mesh[] meshes;
60 
61         foreach(meshIndex; 0 .. scene.mNumMeshes) {
62             auto assimpMesh = scene.mMeshes[meshIndex];
63 
64             aiColor4D* assimpColorSet;
65             if (assimpMesh.mColors[0]) {
66                 assimpColorSet = cast(aiColor4D*) assimpMesh.mColors[0];
67             }
68 
69             aiVector3D* assimpTextureCoordinates;
70             if (assimpMesh.mTextureCoords[0]) {
71                 assimpTextureCoordinates = cast(aiVector3D*) assimpMesh.mTextureCoords[0];
72             }
73 
74 
75             Vertex[] vertices;
76             foreach(vertexIndex; 0 .. assimpMesh.mNumVertices) {
77                 auto assimpVertex = assimpMesh.mVertices[vertexIndex];
78 
79                 float red = 1;
80                 float green = 1;
81                 float blue = 1;
82                 float alpha = 1;
83                 if (assimpColorSet) {
84                     auto color = assimpColorSet[vertexIndex];
85                     red = color.r;
86                     green = color.g;
87                     blue = color.b;
88                     alpha = color.a;
89                 }
90 
91                 float u = 0;
92                 float v = 0;
93                 if (assimpTextureCoordinates) {
94                     u = assimpTextureCoordinates[vertexIndex].x;
95                     v = assimpTextureCoordinates[vertexIndex].y;
96                 }
97 
98                 vertices ~= Vertex(assimpVertex.x, assimpVertex.y, assimpVertex.z, 1,    red, green, blue, alpha,    u, v);
99             }
100 
101             Face[] faces;
102             foreach(faceIndex; 0 .. assimpMesh.mNumFaces) {
103                 auto assimpFace = assimpMesh.mFaces[faceIndex];
104                 if (assimpFace.mNumIndices != 3) {
105                     throw new ModelCreationException(format("Face in mesh contains an unexpect amount of indices (expected 3, got %s), is the mesh triangulated?", assimpFace.mNumIndices));
106                 }
107 
108                 faces ~= Face(assimpFace.mIndices[0], assimpFace.mIndices[1], assimpFace.mIndices[2]);
109             }
110 
111             meshes ~= Mesh(cast(immutable Vertex[]) vertices, cast(immutable Face[]) faces);
112         }
113 
114         return new OpenGlModel(cast(immutable Mesh[]) meshes);
115     }
116 
117 }
118 
119 }
120 }