pipeline generator additions
[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         return mesh;
209 }
210
211 static Material *load_material(const aiScene *ascene, Scene *scene, unsigned int index, const char *tex_path)
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         mat->specular = Vec3(color.r, color.g, color.b);
236
237         aiGetMaterialFloat(amat, AI_MATKEY_SHININESS, &mat->shininess);
238         mat->shininess /= 128.0;
239
240         printf("shininess: %f\n", mat->shininess);
241         aiString tex_name;
242         if(aiGetMaterialString(amat, AI_MATKEY_TEXTURE_DIFFUSE(0), &tex_name) == aiReturn_SUCCESS) {
243                 /* different scene objects might use the same texture, we shouldn't store it twice*/
244
245                 std::string tex_fname = tex_path ? std::string(tex_path) + "/" + tex_name.data : tex_name.data;
246
247                 mat->dtex = scene->find_texture(tex_fname.c_str());
248                 if(!mat->dtex) {
249                         mat->dtex = gfx_create_texture();
250
251                         if(!mat->dtex->load(tex_fname.c_str())) {
252                                 fprintf(stderr, "Failed to load texture data: %s.\n", tex_fname.c_str());
253                                 delete mat->dtex;
254                                 mat->dtex = 0;
255                         }
256                         else {
257                                 mat->dtex->name = std::string(tex_fname.c_str());
258                         }
259                         printf("Successfully loaded texture: %s\n", tex_fname.c_str());
260                         scene->textures.push_back(mat->dtex);
261                 }
262         }
263
264         return mat;
265 }
266
267 Mesh *Scene::find_mesh(const char *name)
268 {
269         for(size_t i=0; i<meshes.size(); i++) {
270                 if(meshes[i]->name == name) {
271                         return meshes[i];
272                 }
273         }
274         fprintf(stderr, "Mesh %s not found.\n", name);
275         return 0;
276 }
277
278 Material *Scene::find_material(const char *name)
279 {
280         for(size_t i=0; i<materials.size(); i++) {
281                 if(materials[i]->name == name) {
282                         return materials[i];
283                 }
284         }
285         fprintf(stderr, "Material %s not found.\n", name);
286         return 0;
287 }
288
289 Texture *Scene::find_texture(const char *name)
290 {
291         for(size_t i=0; i<textures.size(); i++) {
292                 if(textures[i]->name == name) {
293                         return textures[i];
294                 }
295         }
296         return 0;
297 }