11281c37154edddba91fd9c8c74fcc6ed4de1fc3
[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 "gfxapi.h"
14
15 #include "mesh.h"
16 #include "object.h"
17 #include "scene.h"
18 #include "texture.h"
19
20 static Mesh *load_mesh(const aiScene *scene, unsigned int index);
21 static Material *load_material(const aiScene *ascene, Scene *scene, unsigned int index, const char *tex_fname);
22
23 static Mat4 aiMatrix2Mat(aiMatrix4x4 t);
24 static void create_object(aiNode *node, Mat4 transform, Scene *scene, const aiScene *ascene);
25
26 Scene::Scene() {}
27
28 Scene::~Scene()
29 {
30         for(size_t i=0; i<meshes.size(); i++)
31                 delete meshes[i];
32         meshes.clear();
33
34         for(size_t i=0; i<materials.size(); i++)
35                 delete materials[i];
36         materials.clear();
37
38         for(size_t i=0; i<textures.size(); i++)
39                 delete textures[i];
40         textures.clear();
41
42         for(size_t i=0; i<objects.size(); i++)
43                 delete objects[i];
44         objects.clear();
45 }
46
47 bool Scene::load(const char *fname)
48 {
49         /* loading scene */
50         /* TODO !!!!!!!!!!!!!!!! flipuvs might need to be removed + fix the cow */
51         unsigned int ai_flags = aiProcessPreset_TargetRealtime_Quality | aiProcess_FlipUVs;
52         const aiScene *scene = aiImportFile(fname, ai_flags);
53         if(!scene) {
54                 fprintf(stderr, "Failed to import scene: %s\n", fname);
55                 return false;
56         }
57
58         /* load meshes */
59         for(unsigned int i=0; i<scene->mNumMeshes; i++) {
60                 Mesh *mesh = load_mesh(scene, i);
61                 if(!mesh) {
62                         fprintf(stderr, "Failed to load mesh no: %d.\n", i);
63                         return false;
64                 }
65                 mesh->update_vertex_data();
66                 meshes.push_back(mesh);
67         }
68
69         /* load materials */
70         char *tex_path = new char[strlen(fname) + 1];
71         strcpy(tex_path, fname);
72         char *last_slash = strrchr(tex_path, '/');
73         if(last_slash) {
74                 *last_slash = '\0';
75         }
76         else {
77                 delete [] tex_path;
78                 tex_path = 0;
79         }
80
81         for(unsigned int i=0; i<scene->mNumMaterials; i++) {
82                 Material *material = load_material(scene, this, i, tex_path);
83                 if(!material) {
84                         fprintf(stderr, "Failed to load material no: %d", i);
85                         return false;
86                 }
87                 materials.push_back(material);
88         }
89
90         /* create objects */
91         aiNode *node = scene->mRootNode;
92         Mat4 transform = Mat4::identity;
93         create_object(node, transform, this, scene);
94         return true;
95 }
96
97 static Mat4 aiMatrix2Mat(aiMatrix4x4 t)
98 {
99         return Mat4(t.a1, t.a2, t.a3, t.a4,
100                     t.b1, t.b2, t.b3, t.b4,
101                     t.c1, t.c2, t.c3, t.c4,
102                     t.d1, t.d2, t.d3, t.d4);
103 }
104
105 static void create_object(aiNode *node, Mat4 parent_transform, Scene *scene, const aiScene *ascene)
106 {
107         /* Note:
108            The 99% of the scenes have 1 mesh per node => for simplicity we only check the 1st one.
109            Also: the 3D models we are going to use for this demo, have flat structure (no hierarchy)
110            but just in case we need to replace them later, we calculate the transform by assuming that we
111            have a node hierarchy. This => that each object's modelling transformation is the
112            product of its local transformation (mTransformation) with the acc parent nodes transformations
113            (parent_transform)
114         */
115         Mat4 modelling_transform = parent_transform * aiMatrix2Mat(node->mTransformation);
116
117         if(node->mNumMeshes > 0) {
118                 Object *object = new Object;
119
120                 // get the mesh index from node
121                 int mesh_idx = node->mMeshes[0];
122                 object->mesh = scene->meshes[mesh_idx];
123
124                 // get the material index from the mesh that is assigned to this node
125                 aiMesh *amesh = ascene->mMeshes[mesh_idx];
126                 object->material = scene->materials[amesh->mMaterialIndex];
127
128                 // set the object's transformation
129                 object->transform = modelling_transform;
130                 scene->objects.push_back(object);
131         }
132
133         for(unsigned int i=0; i<node->mNumChildren; i++) {
134                 create_object(node->mChildren[i], modelling_transform, scene, ascene);
135         }
136 }
137
138 static Mesh *load_mesh(const aiScene *scene, unsigned int index)
139 {
140         /* load mesh with index from scene using assimp */
141         aiMesh *amesh = scene->mMeshes[index];
142         if(!amesh) {
143                 fprintf(stderr, "Failed to load assimp mesh no: %d.\n", index);
144                 return 0;
145         }
146
147         Mesh *mesh = gfx_create_mesh();
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                         printf("mesh has no texture coordinates.\n");
183                         mesh->tex_coords.push_back(Vec2(0, 0));
184                 }
185
186                 /* tangents */
187                 // if(amesh->mTangents) {
188                 //      mesh->which_mask |= MESH_TANGENT;
189                 //      Vec3 tangent = Vec3(amesh->mTangents[i].x, amesh->mTangents[i].y,
190                 //                          amesh->mTangents[i].z);
191                 //      mesh->tangents.push_back(tangent);
192                 // }
193                 // else {
194                 //      mesh->tangents.push_back(Vec3(1, 0, 0));
195                 // }
196         }
197         /* indices (called faces in assimp) */
198         if(amesh->HasFaces()) {
199                 for(unsigned int i=0; i<amesh->mNumFaces; i++) {
200                         mesh->indices.push_back(amesh->mFaces[i].mIndices[0]);
201                         mesh->indices.push_back(amesh->mFaces[i].mIndices[1]);
202                         mesh->indices.push_back(amesh->mFaces[i].mIndices[2]);
203                 }
204         }
205         else
206                 fprintf(stderr, "No faces found.\n");
207
208         mesh->mat_idx = amesh->mMaterialIndex;
209         return mesh;
210 }
211
212 static Material *load_material(const aiScene *ascene, Scene *scene, unsigned int index, const char *tex_path)
213 {
214         aiMaterial *amat = ascene->mMaterials[index];
215         if(!amat) {
216                 fprintf(stderr, "Failed to load material no: %d.\n", index);
217                 return 0;
218         }
219
220         Material *mat = new Material;
221         mat->dtex = 0;
222         mat->shininess = 40;
223
224         aiString name;
225         amat->Get(AI_MATKEY_NAME, name);
226         mat->name = std::string(name.data);
227
228         aiColor4D color;
229         aiGetMaterialColor(amat, AI_MATKEY_COLOR_DIFFUSE, &color);
230         mat->diffuse = Vec3(color.r, color.g, color.b);
231
232         aiGetMaterialColor(amat, AI_MATKEY_COLOR_SPECULAR, &color);
233         // float spstr;
234         // aiGetMaterialFloat(amat, AI_MATKEY_SHININESS_STRENGTH, &spstr);
235         // mat->specular = spstr * Vec3(color.r, color.g, color.b);
236         mat->specular = Vec3(color.r, color.g, color.b);
237
238         aiGetMaterialFloat(amat, AI_MATKEY_SHININESS, &mat->shininess);
239         mat->shininess /= 128.0;
240
241         printf("shininess: %f\n", mat->shininess);
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
246                 std::string tex_fname = tex_path ? std::string(tex_path) + "/" + tex_name.data : tex_name.data;
247
248                 mat->dtex = scene->find_texture(tex_fname.c_str());
249                 if(!mat->dtex) {
250                         mat->dtex = gfx_create_texture();
251
252                         if(!mat->dtex->load(tex_fname.c_str())) {
253                                 fprintf(stderr, "Failed to load texture data: %s.\n", tex_fname.c_str());
254                                 delete mat->dtex;
255                                 mat->dtex = 0;
256                         }
257                         else {
258                                 mat->dtex->name = std::string(tex_fname.c_str());
259                         }
260                         printf("Successfully loaded texture: %s\n", tex_fname.c_str());
261                         scene->textures.push_back(mat->dtex);
262                 }
263         }
264
265         return mat;
266 }
267
268 Mesh *Scene::find_mesh(const char *name)
269 {
270         for(size_t i=0; i<meshes.size(); i++) {
271                 if(meshes[i]->name == name) {
272                         return meshes[i];
273                 }
274         }
275         fprintf(stderr, "Mesh %s not found.\n", name);
276         return 0;
277 }
278
279 Material *Scene::find_material(const char *name)
280 {
281         for(size_t i=0; i<materials.size(); i++) {
282                 if(materials[i]->name == name) {
283                         return materials[i];
284                 }
285         }
286         fprintf(stderr, "Material %s not found.\n", name);
287         return 0;
288 }
289
290 Texture *Scene::find_texture(const char *name)
291 {
292         for(size_t i=0; i<textures.size(); i++) {
293                 if(textures[i]->name == name) {
294                         return textures[i];
295                 }
296         }
297         return 0;
298 }