quick backup of the finished scene loader before I submit other parts
[demo] / src / scene.cc
1 #include <assert.h>
2
3 #include <assimp/cimport.h>
4 #include <assimp/material.h>
5 #include <assimp/mesh.h>
6 #include <assimp/postprocess.h>
7 #include <assimp/scene.h>
8
9 #include <string>
10
11 #include <gmath/gmath.h>
12
13 #include "mesh.h"
14 #include "object.h"
15 #include "scene.h"
16 #include "texture.h"
17
18 #include "opengl/mesh-gl.h"
19 #include "vulkan/mesh-vk.h"
20
21 #include "opengl/texture-gl.h"
22 #include "vulkan/texture-vk.h"
23
24 extern bool use_vulkan;
25 static Mesh *load_mesh(const aiScene *scene, unsigned int index);
26 static Material *load_material(const aiScene *ascene, Scene *scene, unsigned int index);
27
28 static Mat4 aiMatrix2Mat(aiMatrix4x4 t);
29 static void create_object(aiNode *node, Mat4 transform, Scene *scene, const aiScene *ascene);
30
31 Scene::Scene() {}
32
33 Scene::~Scene()
34 {
35         for(size_t i=0; i<meshes.size(); ++i)
36                 delete meshes[i];
37         meshes.clear();
38
39         for(size_t i=0; i<materials.size(); ++i)
40                 delete materials[i];
41         materials.clear();
42
43         for(size_t i=0; i<textures.size(); ++i)
44                 delete textures[i];
45         textures.clear();
46
47         for(size_t i=0; i<objects.size(); ++i)
48                 delete objects[i];
49         objects.clear();
50 }
51
52 bool Scene::load(const char *fname)
53 {
54         /* loading scene */
55         unsigned int ai_flags = aiProcessPreset_TargetRealtime_Quality;
56         const aiScene *scene = aiImportFile(fname, ai_flags);
57         if(!scene) {
58                 fprintf(stderr, "Failed to import scene: %s\n", fname);
59                 return false;
60         }
61
62         /* load meshes */
63         for(unsigned int i=0; i<scene->mNumMeshes; ++i) {
64                 Mesh *mesh = load_mesh(scene, i);
65                 if(!mesh) {
66                         fprintf(stderr, "Failed to load mesh no: %d.\n", i);
67                         return false;
68                 }
69                 meshes.push_back(mesh);
70         }
71
72         /* load materials */
73         for(unsigned int i=0; i<scene->mNumMaterials; ++i) {
74                 Material *material = load_material(scene, this, i);
75                 if(!material) {
76                         fprintf(stderr, "Failed to load material no: %d", i);
77                         return false;
78                 }
79                 materials.push_back(material);
80         }
81
82         /* create objects */
83         aiNode *node = scene->mRootNode;
84         Mat4 transform = Mat4::identity;
85         create_object(node, transform, this, scene);
86         return true;
87 }
88
89 static Mat4 aiMatrix2Mat(aiMatrix4x4 t)
90 {
91         return Mat4(t.a1, t.a2, t.a3, t.a4,
92                     t.b1, t.b2, t.b3, t.b4,
93                     t.c1, t.c2, t.c3, t.c4,
94                     t.d1, t.d2, t.d3, t.d4);
95 }
96
97 static void create_object(aiNode *node, Mat4 parent_transform, Scene *scene, const aiScene *ascene)
98 {
99         /* Note:
100            The 99% of the scenes have 1 mesh per node => for simplicity we only check the 1st one.
101            Also: the 3D models we are going to use for this demo, have flat structure (no hierarchy)
102            but just in case we need to replace them later, we calculate the transform by assuming that we
103            have a node hierarchy. This => that each object's modelling transformation is the 
104            product of its local transformation (mTransformation) with the acc parent nodes transformations
105            (parent_transform)
106         */
107         Mat4 modelling_transform = parent_transform * aiMatrix2Mat(node->mTransformation);
108
109         if(node->mNumMeshes > 0) {
110                 Object *object = new Object;
111
112                 // get the mesh index from node
113                 int mesh_idx = node->mMeshes[0];
114                 object->mesh = scene->meshes[mesh_idx];
115
116                 // get the material index from the mesh that is assigned to this node
117                 aiMesh *amesh = ascene->mMeshes[mesh_idx];
118                 object->material = scene->materials[amesh->mMaterialIndex];
119
120                 // set the object's transformation
121                 object->transform = modelling_transform;
122                 scene->objects.push_back(object);
123         }
124
125         for(unsigned int i=0; i<node->mNumChildren; ++i) {
126                 create_object(node->mChildren[i], modelling_transform, scene, ascene);
127         }
128 }
129
130 static Mesh *load_mesh(const aiScene *scene, unsigned int index)
131 {
132         /* load mesh with index from scene using assimp */
133         aiMesh *amesh = scene->mMeshes[index];
134         if(!amesh) {
135                 fprintf(stderr, "Failed to load assimp mesh no: %d.\n", index);
136                 return 0;
137         }
138
139         Mesh *mesh;
140         if(use_vulkan) {
141                 mesh = new MeshVK;
142         }
143         else {
144                 mesh = new MeshGL;
145         }
146
147         mesh->name = std::string(amesh->mName.data);
148         mesh->which_mask = 0;
149
150         for(unsigned int i=0; i<amesh->mNumVertices; ++i) {
151                 /* vertices */
152                 if(amesh->HasPositions()) {
153                         mesh->which_mask |= MESH_VERTEX;
154                         Vec3 vertex = Vec3(amesh->mVertices[i].x, amesh->mVertices[i].y,
155                                            amesh->mVertices[i].z);
156
157                         mesh->vertices.push_back(vertex);
158                 }
159                 else {
160                         fprintf(stderr, "Mesh has no geometry!\n");
161                         delete mesh;
162                         return 0;
163                 }
164
165                 /* normals */
166                 if(amesh->HasNormals()) {
167                         mesh->which_mask |= MESH_NORMAL;
168                         Vec3 normal = Vec3(amesh->mNormals[i].x, amesh->mNormals[i].y,
169                                            amesh->mNormals[i].z);
170                         mesh->normals.push_back(normal);
171                 }
172                 else {
173                         fprintf(stderr, "Mesh has no normals!\n");
174                         delete mesh;
175                         return 0;
176                 }
177
178                 /* texture coordinates */
179                 if(amesh->mTextureCoords[0]) {
180                         mesh->which_mask |= MESH_TEXTURE;
181                         Vec2 tex_coord = Vec2(amesh->mTextureCoords[0][i].x, amesh->mTextureCoords[0][i].y);
182                         mesh->tex_coords.push_back(tex_coord);
183                 }
184                 else {
185                         mesh->tex_coords.push_back(Vec2(0, 0));
186                 }
187
188                 /* tangents */
189                 if(amesh->mTangents) {
190                         mesh->which_mask |= MESH_TANGENT;
191                         Vec3 tangent = Vec3(amesh->mTangents[i].x, amesh->mTangents[i].y,
192                                             amesh->mTangents[i].z);
193                         mesh->tangents.push_back(tangent);
194                 }
195                 else {
196                         mesh->tangents.push_back(Vec3(1, 0, 0));
197                 }
198         }
199         /* indices (called faces in assimp) */
200         if(amesh->HasFaces()) {
201                 mesh->which_mask |= MESH_INDEX;
202                 for(unsigned int i=0; i<amesh->mNumFaces; ++i) {
203                         mesh->indices.push_back(amesh->mFaces[i].mIndices[0]);
204                         mesh->indices.push_back(amesh->mFaces[i].mIndices[1]);
205                         mesh->indices.push_back(amesh->mFaces[i].mIndices[2]);
206                 }
207         }
208         else
209                 fprintf(stderr, "No faces found.\n");
210
211         mesh->mat_idx = amesh->mMaterialIndex;
212         return mesh;
213 }
214
215 static Material *load_material(const aiScene *ascene, Scene *scene, unsigned int index)
216 {
217         aiMaterial *amat = ascene->mMaterials[index];
218         if(!amat) {
219                 fprintf(stderr, "Failed to load material no: %d.\n", index);
220                 return 0;
221         }
222
223         Material *mat = new Material;
224         mat->dtex = 0;
225         mat->shininess = 40;
226
227         aiString name;
228         amat->Get(AI_MATKEY_NAME, name);
229         mat->name = std::string(name.data);
230
231         aiColor4D color;
232         aiGetMaterialColor(amat, AI_MATKEY_COLOR_DIFFUSE, &color);
233         mat->diffuse = Vec3(color.r, color.g, color.b);
234
235         aiGetMaterialColor(amat, AI_MATKEY_COLOR_SPECULAR, &color);
236         float spstr;
237         aiGetMaterialFloat(amat, AI_MATKEY_SHININESS_STRENGTH, &spstr);
238         mat->specular = spstr * Vec3(color.r, color.g, color.b);
239
240         aiGetMaterialFloat(amat, AI_MATKEY_SHININESS, &mat->shininess);
241
242         aiString tex_name;
243         if(aiGetMaterialString(amat, AI_MATKEY_TEXTURE_DIFFUSE(0), &tex_name) == aiReturn_SUCCESS) {
244                 /* different scene objects might use the same texture, we shouldn't store it twice*/
245                 //mat->dtex = scene->find_texture(tex_name.data);
246                 if(!mat->dtex) {
247                         if(use_vulkan) {
248                                 mat->dtex = new TextureVK;
249                         }
250                         else {
251                                 mat->dtex = new TextureGL;
252                         }
253                         if(!mat->dtex->load(tex_name.data)) {
254                                 fprintf(stderr, "Failed to load texture data: %s.\n", tex_name.data);
255                                 delete mat->dtex;
256                                 mat->dtex = 0;
257                         }
258                         scene->textures.push_back(mat->dtex);
259                 }
260         }
261
262         return mat;
263 }