- shaders for both lightmapped objects with or without albedo maps
[laserbrain_demo] / src / sceneload.cc
1 #include <stdio.h>
2 #include <assert.h>
3 #include <vector>
4 #include <map>
5 #include <gmath/gmath.h>
6 #include <assimp/cimport.h>
7 #include <assimp/postprocess.h>
8 #include <assimp/scene.h>
9 #include <assimp/mesh.h>
10 #include <assimp/material.h>
11 #include <assimp/anim.h>
12 #include <assimp/vector3.h>
13 #include <assimp/matrix4x4.h>
14 #include <assimp/quaternion.h>
15 #include "scene.h"
16 #include "objmesh.h"
17 #include "datamap.h"
18 #include "logger.h"
19
20 static bool load_material(Scene *scn, Material *mat, const aiMaterial *aimat);
21 static SceneNode *load_node(Scene *scn, const aiScene *aiscn, unsigned int flags, const aiNode *ainode);
22 static Mesh *load_mesh(Scene *scn, const aiScene *aiscn, unsigned int flags, const aiMesh *aimesh);
23 /*static const char *mprop_semantic(int x);
24 static int count_textures(const aiMaterial *aimat);*/
25 static int assimp_textype(aiTextureType type);
26 static const char *assimp_textypestr(aiTextureType type);
27
28 static Mat4 assimp_matrix(const aiMatrix4x4 &aim);
29
30 /*static Vec3 assimp_vector(const aiVector3D &v);
31 static Quat assimp_quat(const aiQuaternion &q);
32 static long assimp_time(const aiAnimation *anim, double aitime);
33 static void print_hierarchy(const aiNode *node);
34 */
35
36 static std::map<std::string, SceneNode*> node_by_name;
37 static std::map<aiMesh*, Mesh*> mesh_by_aimesh;
38
39 bool Scene::load(const char *fname, unsigned int flags)
40 {
41         unsigned int ppflags = aiProcess_CalcTangentSpace |
42                 aiProcess_GenNormals |
43                 aiProcess_JoinIdenticalVertices |
44                 aiProcess_Triangulate |
45                 aiProcess_SortByPType |
46                 aiProcess_GenUVCoords |
47                 //aiProcess_PreTransformVertices |
48                 aiProcess_TransformUVCoords;
49
50         if(flags & SCNLOAD_FLIPTEX) {
51                 ppflags |= aiProcess_FlipUVs;
52         }
53
54         info_log("Loading scene file: %s\n", fname);
55
56         const aiScene *aiscn = aiImportFile(fname, ppflags);
57         if(!aiscn) {
58                 error_log("failed to load scene file: %s\n", fname);
59                 return false;
60         }
61
62         // assimp adds its own root node, which might have transformations
63         Vec3 root_pos, root_scaling(1.0, 1.0, 1.0);
64         Quat root_rot;
65
66         if(aiscn->mRootNode) {
67                 Mat4 root_matrix = assimp_matrix(aiscn->mRootNode->mTransformation);
68                 root_pos = root_matrix.get_translation();
69                 root_rot = root_matrix.get_rotation();
70                 root_scaling = root_matrix.get_scaling();
71         }
72
73         // load all meshes
74         for(unsigned int i=0; i<aiscn->mNumMeshes; i++) {
75                 aiMesh *aimesh = aiscn->mMeshes[i];
76                 Mesh *mesh;
77
78                 switch(aimesh->mPrimitiveTypes) {
79                 case aiPrimitiveType_TRIANGLE:
80                         if((mesh = load_mesh(this, aiscn, flags, aimesh))) {
81                                 mesh_by_aimesh[aimesh] = mesh;
82                                 meshes.push_back(mesh);
83                         }
84                         break;
85
86                 default:
87                         error_log("unsupported primitive type: %u\n", aimesh->mPrimitiveTypes);
88                         break;
89                 }
90         }
91
92         if(!nodes) {
93                 nodes = new SceneNode;
94                 nodes->scene = this;
95                 nodes->set_name("root");
96                 nodes->set_position(root_pos);
97                 nodes->set_rotation(root_rot);
98                 nodes->set_scaling(root_scaling);
99         }
100
101         // load all the nodes recursively
102         for(unsigned int i=0; i<aiscn->mRootNode->mNumChildren; i++) {
103                 SceneNode *node = load_node(this, aiscn, flags, aiscn->mRootNode->mChildren[i]);
104                 if(node) {
105                         nodes->add_child(node);
106                 }
107         }
108
109         node_by_name.clear();
110         mesh_by_aimesh.clear();
111
112         aiReleaseImport(aiscn);
113         info_log("loaded scene file: %s, %d meshes\n", fname, (int)meshes.size());
114         nodes->update(0);
115         return true;
116 }
117
118 static bool load_material(Scene *scn, Material *mat, const aiMaterial *aimat)
119 {
120         aiString name;
121         aiColor4D aicol;
122         float shin, shin_str;
123
124         if(aiGetMaterialString(aimat, AI_MATKEY_NAME, &name) == 0) {
125                 mat->name = name.data;
126         } else {
127                 mat->name = "unknown";
128         }
129         //info_log("load_material: %s\n", mat->name.c_str());
130
131         if(aiGetMaterialColor(aimat, AI_MATKEY_COLOR_DIFFUSE, &aicol) == 0) {
132                 mat->diffuse = Vec3(aicol[0], aicol[1], aicol[2]);
133         }
134         if(aiGetMaterialColor(aimat, AI_MATKEY_COLOR_SPECULAR, &aicol) == 0) {
135                 mat->specular = Vec3(aicol[0], aicol[1], aicol[2]);
136         }
137
138         unsigned int count = 1;
139         if(aiGetMaterialFloatArray(aimat, AI_MATKEY_SHININESS_STRENGTH, &shin_str, &count) != 0) {
140                 shin_str = 1.0;
141         }
142         if(aiGetMaterialFloatArray(aimat, AI_MATKEY_SHININESS, &shin, &count) == 0) {
143                 // XXX can't remember how I came up with this...
144                 mat->shininess = shin * shin_str * 0.0001 * 128.0;
145         }
146
147         // load textures
148
149         const int num_tex_types = aiTextureType_UNKNOWN + 1;
150         for(int i=0; i<num_tex_types; i++) {
151                 aiTextureType aitype = (aiTextureType)i;
152                 int count = aiGetMaterialTextureCount(aimat, aitype);
153
154                 for(int j=0; j<count; j++) {
155                         aiString aipath;
156                         if(aiGetMaterialTexture(aimat, aitype, j, &aipath) != 0) {
157                                 continue;
158                         }
159
160                         char *fname;
161                         int nsize = datamap_path_size(aipath.data);
162                         if(nsize) {
163                                 fname = new char[nsize];
164                                 datamap_lookup(aipath.data, fname, nsize);
165                         } else {
166                                 fname = new char[strlen(aipath.data) + 1];
167                                 char *dptr = fname;
168                                 char *sptr = aipath.data;
169                                 do {
170                                         *dptr++ = *sptr == '\\' ? '/' : *sptr;
171                                 } while(*sptr++);
172                         }
173
174                         int textype = assimp_textype(aitype);
175                         info_log("loading %s texture: %s\n", assimp_textypestr(aitype), fname);
176
177                         Texture *tex = scn->texset->get_texture(fname, TEX_2D);
178                         assert(tex);
179                         mat->textures.push_back(tex);
180
181                         if(textype != MTL_TEX_UNKNOWN && !mat->stdtex[textype]) {
182                                 mat->stdtex[textype] = tex;
183                         }
184                 }
185         }
186
187         return true;
188 }
189
190 static SceneNode *load_node(Scene *scn, const aiScene *aiscn, unsigned int flags, const aiNode *ainode)
191 {
192         SceneNode *node = new SceneNode;
193         node->set_name(ainode->mName.data);
194
195         // transformation
196         Mat4 matrix = assimp_matrix(ainode->mTransformation);
197         Vec3 pos = matrix.get_translation();
198         Quat rot = matrix.get_rotation();
199         Vec3 scale = matrix.get_scaling();
200
201         node->set_position(pos);
202         node->set_rotation(rot);
203         node->set_scaling(scale);
204         node->dbg_xform = matrix;
205
206         // meshes
207         for(unsigned int i=0; i<ainode->mNumMeshes; i++) {
208                 aiMesh *aimesh = aiscn->mMeshes[ainode->mMeshes[i]];
209
210                 Mesh *mesh = mesh_by_aimesh[aimesh];
211                 if(mesh) {
212                         ObjMesh *obj = new ObjMesh;
213                         obj->set_name(mesh->get_name());
214                         obj->mesh = mesh;
215                         // also grab the material of this mesh
216                         load_material(scn, &obj->mtl, aiscn->mMaterials[aimesh->mMaterialIndex]);
217
218                         node->add_object(obj);
219                         scn->objects.push_back(obj);
220                 }
221         }
222
223         /* recurse to all children */
224         for(unsigned int i=0; i<ainode->mNumChildren; i++) {
225                 SceneNode *child = load_node(scn, aiscn, flags, ainode->mChildren[i]);
226                 if(child) {
227                         node->add_child(child);
228                 }
229         }
230
231         node_by_name[node->get_name()] = node;
232         return node;
233 }
234
235 static Mesh *load_mesh(Scene *scn, const aiScene *aiscn, unsigned int flags, const aiMesh *aimesh)
236 {
237         Mesh *mesh = new Mesh;
238         mesh->set_name(aimesh->mName.data);
239
240         int num_verts = aimesh->mNumVertices;
241         int num_faces = aimesh->mNumFaces;
242
243         mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, (float*)aimesh->mVertices);
244
245         if(aimesh->mNormals) {
246                 mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, (float*)aimesh->mNormals);
247         }
248         if(aimesh->mTangents) {
249                 mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, (float*)aimesh->mTangents);
250         }
251         if(aimesh->mTextureCoords[0]) {
252                 mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 3, num_verts, (float*)aimesh->mTextureCoords[0]);
253         }
254         if(aimesh->mTextureCoords[1]) {
255                 mesh->set_attrib_data(MESH_ATTR_TEXCOORD2, 3, num_verts, (float*)aimesh->mTextureCoords[1]);
256         }
257
258         if(flags & SCNLOAD_FLIPYZ) {
259                 Vec3 *vptr = (Vec3*)mesh->get_attrib_data(MESH_ATTR_VERTEX);
260                 for(int i=0; i<num_verts; i++) {
261                         *vptr = vptr->xzy();
262                         ++vptr;
263                 }
264
265                 Vec3 *nptr = (Vec3*)mesh->get_attrib_data(MESH_ATTR_NORMAL);
266                 for(int i=0; i<num_verts; i++) {
267                         *nptr = nptr->xzy();
268                         ++nptr;
269                 }
270
271                 Vec3 *tptr = (Vec3*)mesh->get_attrib_data(MESH_ATTR_TANGENT);
272                 for(int i=0; i<num_verts; i++) {
273                         *tptr = tptr->xzy();
274                         ++tptr;
275                 }
276         }
277
278         unsigned int *iptr = mesh->set_index_data(num_faces * 3);
279         for(int i=0; i<num_faces; i++) {
280                 iptr[0] = aimesh->mFaces[i].mIndices[0];
281                 iptr[1] = aimesh->mFaces[i].mIndices[flags & SCNLOAD_FLIPYZ ? 2 : 1];
282                 iptr[2] = aimesh->mFaces[i].mIndices[flags & SCNLOAD_FLIPYZ ? 1 : 2];
283                 iptr += 3;
284         }
285         return mesh;
286 }
287
288 static int assimp_textype(aiTextureType type)
289 {
290         switch(type) {
291         case aiTextureType_DIFFUSE:
292                 return MTL_TEX_DIFFUSE;
293         case aiTextureType_SPECULAR:
294                 return MTL_TEX_SPECULAR;
295         case aiTextureType_NORMALS:
296                 return MTL_TEX_NORMALMAP;
297         case aiTextureType_LIGHTMAP:
298         case aiTextureType_EMISSIVE:
299                 return MTL_TEX_LIGHTMAP;
300         case aiTextureType_REFLECTION:
301                 return MTL_TEX_ENVMAP;
302         default:
303                 break;
304         }
305         return MTL_TEX_UNKNOWN;
306 }
307
308 static const char *assimp_textypestr(aiTextureType type)
309 {
310         switch(type) {
311         case aiTextureType_DIFFUSE:
312                 return "diffuse";
313         case aiTextureType_SPECULAR:
314                 return "specular";
315         case aiTextureType_NORMALS:
316                 return "normalmap";
317         case aiTextureType_LIGHTMAP:
318         case aiTextureType_EMISSIVE:
319                 return "lightmap";
320         case aiTextureType_REFLECTION:
321                 return "envmap";
322         default:
323                 break;
324         }
325         return "unknown";
326 }
327
328 static Mat4 assimp_matrix(const aiMatrix4x4 &aim)
329 {
330         Mat4 m;
331         memcpy(m[0], &aim, 16 * sizeof(float));
332         return transpose(m);
333 }