c13e0814f95fc191d24a7d0354cb76c36a84300f
[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                 mesh->update_vertex_data();
70                 meshes.push_back(mesh);
71         }
72
73         /* load materials */
74         for(unsigned int i=0; i<scene->mNumMaterials; ++i) {
75                 Material *material = load_material(scene, this, i);
76                 if(!material) {
77                         fprintf(stderr, "Failed to load material no: %d", i);
78                         return false;
79                 }
80                 materials.push_back(material);
81         }
82
83         /* create objects */
84         aiNode *node = scene->mRootNode;
85         Mat4 transform = Mat4::identity;
86         create_object(node, transform, this, scene);
87         return true;
88 }
89
90 static Mat4 aiMatrix2Mat(aiMatrix4x4 t)
91 {
92         return Mat4(t.a1, t.a2, t.a3, t.a4,
93                     t.b1, t.b2, t.b3, t.b4,
94                     t.c1, t.c2, t.c3, t.c4,
95                     t.d1, t.d2, t.d3, t.d4);
96 }
97
98 static void create_object(aiNode *node, Mat4 parent_transform, Scene *scene, const aiScene *ascene)
99 {
100         /* Note:
101            The 99% of the scenes have 1 mesh per node => for simplicity we only check the 1st one.
102            Also: the 3D models we are going to use for this demo, have flat structure (no hierarchy)
103            but just in case we need to replace them later, we calculate the transform by assuming that we
104            have a node hierarchy. This => that each object's modelling transformation is the
105            product of its local transformation (mTransformation) with the acc parent nodes transformations
106            (parent_transform)
107         */
108         Mat4 modelling_transform = parent_transform * aiMatrix2Mat(node->mTransformation);
109
110         if(node->mNumMeshes > 0) {
111                 Object *object = new Object;
112
113                 // get the mesh index from node
114                 int mesh_idx = node->mMeshes[0];
115                 object->mesh = scene->meshes[mesh_idx];
116
117                 // get the material index from the mesh that is assigned to this node
118                 aiMesh *amesh = ascene->mMeshes[mesh_idx];
119                 object->material = scene->materials[amesh->mMaterialIndex];
120
121                 // set the object's transformation
122                 object->transform = modelling_transform;
123                 scene->objects.push_back(object);
124         }
125
126         for(unsigned int i=0; i<node->mNumChildren; ++i) {
127                 create_object(node->mChildren[i], modelling_transform, scene, ascene);
128         }
129 }
130
131 static Mesh *load_mesh(const aiScene *scene, unsigned int index)
132 {
133         /* load mesh with index from scene using assimp */
134         aiMesh *amesh = scene->mMeshes[index];
135         if(!amesh) {
136                 fprintf(stderr, "Failed to load assimp mesh no: %d.\n", index);
137                 return 0;
138         }
139
140         Mesh *mesh;
141         if(use_vulkan) {
142                 mesh = new MeshVK;
143         }
144         else {
145                 mesh = new MeshGL;
146         }
147
148         mesh->name = std::string(amesh->mName.data);
149
150         for(unsigned int i=0; i<amesh->mNumVertices; ++i) {
151                 /* vertices */
152                 if(amesh->HasPositions()) {
153                         Vec3 vertex = Vec3(amesh->mVertices[i].x, amesh->mVertices[i].y,
154                                            amesh->mVertices[i].z);
155
156                         mesh->vertices.push_back(vertex);
157                 }
158                 else {
159                         fprintf(stderr, "Mesh has no geometry!\n");
160                         delete mesh;
161                         return 0;
162                 }
163
164                 /* normals */
165                 if(amesh->HasNormals()) {
166                         Vec3 normal = Vec3(amesh->mNormals[i].x, amesh->mNormals[i].y,
167                                            amesh->mNormals[i].z);
168                         mesh->normals.push_back(normal);
169                 }
170                 else {
171                         fprintf(stderr, "Mesh has no normals!\n");
172                         delete mesh;
173                         return 0;
174                 }
175
176                 /* texture coordinates */
177                 if(amesh->mTextureCoords[0]) {
178                         Vec2 tex_coord = Vec2(amesh->mTextureCoords[0][i].x, amesh->mTextureCoords[0][i].y);
179                         mesh->tex_coords.push_back(tex_coord);
180                 }
181                 else {
182                         mesh->tex_coords.push_back(Vec2(0, 0));
183                 }
184
185                 /* tangents */
186                 // if(amesh->mTangents) {
187                 //      mesh->which_mask |= MESH_TANGENT;
188                 //      Vec3 tangent = Vec3(amesh->mTangents[i].x, amesh->mTangents[i].y,
189                 //                          amesh->mTangents[i].z);
190                 //      mesh->tangents.push_back(tangent);
191                 // }
192                 // else {
193                 //      mesh->tangents.push_back(Vec3(1, 0, 0));
194                 // }
195         }
196         /* indices (called faces in assimp) */
197         if(amesh->HasFaces()) {
198                 for(unsigned int i=0; i<amesh->mNumFaces; ++i) {
199                         mesh->indices.push_back(amesh->mFaces[i].mIndices[0]);
200                         mesh->indices.push_back(amesh->mFaces[i].mIndices[1]);
201                         mesh->indices.push_back(amesh->mFaces[i].mIndices[2]);
202                 }
203         }
204         else
205                 fprintf(stderr, "No faces found.\n");
206
207         mesh->mat_idx = amesh->mMaterialIndex;
208         return mesh;
209 }
210
211 static Material *load_material(const aiScene *ascene, Scene *scene, unsigned int index)
212 {
213         aiMaterial *amat = ascene->mMaterials[index];
214         if(!amat) {
215                 fprintf(stderr, "Failed to load material no: %d.\n", index);
216                 return 0;
217         }
218
219         Material *mat = new Material;
220         mat->dtex = 0;
221         mat->shininess = 40;
222
223         aiString name;
224         amat->Get(AI_MATKEY_NAME, name);
225         mat->name = std::string(name.data);
226
227         aiColor4D color;
228         aiGetMaterialColor(amat, AI_MATKEY_COLOR_DIFFUSE, &color);
229         mat->diffuse = Vec3(color.r, color.g, color.b);
230
231         aiGetMaterialColor(amat, AI_MATKEY_COLOR_SPECULAR, &color);
232         float spstr;
233         aiGetMaterialFloat(amat, AI_MATKEY_SHININESS_STRENGTH, &spstr);
234         mat->specular = spstr * Vec3(color.r, color.g, color.b);
235
236         aiGetMaterialFloat(amat, AI_MATKEY_SHININESS, &mat->shininess);
237
238         aiString tex_name;
239         if(aiGetMaterialString(amat, AI_MATKEY_TEXTURE_DIFFUSE(0), &tex_name) == aiReturn_SUCCESS) {
240                 /* different scene objects might use the same texture, we shouldn't store it twice*/
241                 //mat->dtex = scene->find_texture(tex_name.data);
242                 if(!mat->dtex) {
243                         if(use_vulkan) {
244                                 mat->dtex = new TextureVK;
245                         }
246                         else {
247                                 mat->dtex = new TextureGL;
248                         }
249                         if(!mat->dtex->load(tex_name.data)) {
250                                 fprintf(stderr, "Failed to load texture data: %s.\n", tex_name.data);
251                                 delete mat->dtex;
252                                 mat->dtex = 0;
253                         }
254                         scene->textures.push_back(mat->dtex);
255                 }
256         }
257
258         return mat;
259 }