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