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