6 #include <gmath/gmath.h>
7 #include <assimp/cimport.h>
8 #include <assimp/cfileio.h>
9 #include <assimp/postprocess.h>
10 #include <assimp/scene.h>
11 #include <assimp/mesh.h>
12 #include <assimp/material.h>
13 #include <assimp/anim.h>
14 #include <assimp/vector3.h>
15 #include <assimp/matrix4x4.h>
16 #include <assimp/quaternion.h>
23 #include "metascene.h"
25 static bool load_material(Scene *scn, Material *mat, const aiMaterial *aimat);
26 static SceneNode *load_node(Scene *scn, const aiScene *aiscn, unsigned int flags, const aiNode *ainode);
27 static Mesh *load_mesh(Scene *scn, const aiScene *aiscn, unsigned int flags, const aiMesh *aimesh);
28 /*static const char *mprop_semantic(int x);
29 static int count_textures(const aiMaterial *aimat);*/
30 static int assimp_textype(aiTextureType type);
31 //static const char *assimp_textypestr(aiTextureType type);
33 static Mat4 assimp_matrix(const aiMatrix4x4 &aim);
35 /*static Vec3 assimp_vector(const aiVector3D &v);
36 static Quat assimp_quat(const aiQuaternion &q);
37 static long assimp_time(const aiAnimation *anim, double aitime);
38 static void print_hierarchy(const aiNode *node);
41 static aiFile *io_open(aiFileIO *io, const char *fname, const char *mode);
42 static void io_close(aiFileIO *io, aiFile *aifp);
43 static size_t io_read(aiFile *aifp, char *buf, size_t size, size_t count);
44 static size_t io_tell(aiFile *aifp);
45 static size_t io_filesize(aiFile *aifp);
46 static aiReturn io_seek(aiFile *aifp, size_t offs, aiOrigin whence);
51 std::map<std::string, SceneNode*> node_by_name;
52 std::map<aiMesh*, Mesh*> mesh_by_aimesh;
55 #define LD_STAGE_MASK 0xf000
57 bool Scene::load(const char *fname, unsigned int flags)
59 if((flags & LD_STAGE_MASK) == 0) {
60 // not passing either of the stage specifiers, means do the whole job
61 flags |= LD_STAGE_MASK;
64 // first perform I/O and all operations not requiring access to an OpenGL context
65 if(flags & SCNLOAD_STAGE_IO) {
66 unsigned int ppflags = aiProcess_CalcTangentSpace |
67 aiProcess_GenNormals |
68 aiProcess_JoinIdenticalVertices |
69 aiProcess_Triangulate |
70 aiProcess_SortByPType |
71 aiProcess_GenUVCoords |
72 //aiProcess_PreTransformVertices |
73 aiProcess_TransformUVCoords;
75 if(flags & SCNLOAD_FLIPTEX) {
76 ppflags |= aiProcess_FlipUVs;
79 info_log("Loading scene file: %s\n", fname);
80 if(this->name.empty()) {
81 this->name = std::string(fname);
85 io.OpenProc = io_open;
86 io.CloseProc = io_close;
88 const aiScene *aiscn = aiImportFileEx(fname, ppflags, &io);
90 error_log("failed to load scene file: %s\n", fname);
94 LoaderData *ldata = new LoaderData;
96 ldata->fname = std::string(fname);
97 loader_data = (void*)ldata;
100 /* then, assuming we have successfully loaded everything, proceed to construct
101 * all the engine objects, which require access to the OpenGL context
103 if(flags & SCNLOAD_STAGE_GL) {
105 error_log("second stage scene loader failed to find valid I/O data\n");
109 LoaderData *ldata = (LoaderData*)loader_data;
110 const aiScene *aiscn = ldata->aiscn;
111 fname = ldata->fname.c_str();
113 clear(); // clear any previous data (TODO: add flag for not clearing)
115 // assimp adds its own root node, which might have transformations
116 Vec3 root_pos, root_scaling(1.0, 1.0, 1.0);
119 if(aiscn->mRootNode) {
120 Mat4 root_matrix = assimp_matrix(aiscn->mRootNode->mTransformation);
121 root_pos = root_matrix.get_translation();
122 root_rot = root_matrix.get_rotation();
123 root_scaling = root_matrix.get_scaling();
127 nodes = new SceneNode;
129 nodes->set_name("root");
130 nodes->set_position(root_pos);
131 nodes->set_rotation(root_rot);
132 nodes->set_scaling(root_scaling);
136 for(unsigned int i=0; i<aiscn->mNumMeshes; i++) {
137 aiMesh *aimesh = aiscn->mMeshes[i];
140 switch(aimesh->mPrimitiveTypes) {
141 case aiPrimitiveType_TRIANGLE:
142 if((mesh = load_mesh(this, aiscn, flags, aimesh))) {
143 ldata->mesh_by_aimesh[aimesh] = mesh;
144 meshes.push_back(mesh);
149 error_log("unsupported primitive type: %u\n", aimesh->mPrimitiveTypes);
154 // load all the nodes recursively
155 for(unsigned int i=0; i<aiscn->mRootNode->mNumChildren; i++) {
156 SceneNode *node = load_node(this, aiscn, flags, aiscn->mRootNode->mChildren[i]);
158 nodes->add_child(node);
162 info_log("loaded scene file: %s, %d meshes\n", fname, (int)meshes.size());
164 aiReleaseImport(aiscn);
172 static bool load_material(Scene *scn, Material *mat, const aiMaterial *aimat)
176 float shin, shin_str;
178 if(aiGetMaterialString(aimat, AI_MATKEY_NAME, &name) == 0) {
179 mat->name = name.data;
181 mat->name = "unknown";
183 //info_log("load_material: %s\n", mat->name.c_str());
185 if(aiGetMaterialColor(aimat, AI_MATKEY_COLOR_DIFFUSE, &aicol) == 0) {
186 mat->diffuse = Vec3(aicol[0], aicol[1], aicol[2]);
188 if(aiGetMaterialColor(aimat, AI_MATKEY_COLOR_SPECULAR, &aicol) == 0) {
189 mat->specular = Vec3(aicol[0], aicol[1], aicol[2]);
192 unsigned int count = 1;
193 if(aiGetMaterialFloatArray(aimat, AI_MATKEY_SHININESS_STRENGTH, &shin_str, &count) != 0) {
196 if(aiGetMaterialFloatArray(aimat, AI_MATKEY_SHININESS, &shin, &count) == 0) {
197 // XXX can't remember how I came up with this...
198 mat->shininess = shin * shin_str * 0.0001 * 128.0;
203 const int num_tex_types = aiTextureType_UNKNOWN + 1;
204 for(int i=0; i<num_tex_types; i++) {
205 aiTextureType aitype = (aiTextureType)i;
206 int count = aiGetMaterialTextureCount(aimat, aitype);
208 for(int j=0; j<count; j++) {
210 if(aiGetMaterialTexture(aimat, aitype, j, &aipath) != 0) {
214 char *fname = (char*)alloca(strlen(aipath.data) + 1);
216 char *sptr = aipath.data;
218 *dptr++ = *sptr == '\\' ? '/' : *sptr;
221 if(!fname || !*fname) continue;
223 int textype = assimp_textype(aitype);
225 Texture *tex = texman.get_texture(fname, TEX_2D, &scn->datamap);
227 mat->textures.push_back(tex);
229 if(textype != MTL_TEX_UNKNOWN && !mat->stdtex[textype]) {
230 mat->stdtex[textype] = tex;
238 static SceneNode *load_node(Scene *scn, const aiScene *aiscn, unsigned int flags, const aiNode *ainode)
240 LoaderData *ldata = (LoaderData*)scn->loader_data;
242 SceneNode *node = new SceneNode;
243 node->set_name(ainode->mName.data);
246 Mat4 matrix = assimp_matrix(ainode->mTransformation);
247 Vec3 pos = matrix.get_translation();
248 Quat rot = matrix.get_rotation();
249 Vec3 scale = matrix.get_scaling();
251 node->set_position(pos);
252 node->set_rotation(rot);
253 node->set_scaling(scale);
254 node->dbg_xform = matrix;
257 for(unsigned int i=0; i<ainode->mNumMeshes; i++) {
258 aiMesh *aimesh = aiscn->mMeshes[ainode->mMeshes[i]];
260 Mesh *mesh = ldata->mesh_by_aimesh[aimesh];
262 ObjMesh *obj = new ObjMesh;
263 obj->set_name(mesh->get_name());
265 // also grab the material of this mesh
266 load_material(scn, &obj->mtl, aiscn->mMaterials[aimesh->mMaterialIndex]);
268 node->add_object(obj);
269 scn->objects.push_back(obj);
273 /* recurse to all children */
274 for(unsigned int i=0; i<ainode->mNumChildren; i++) {
275 SceneNode *child = load_node(scn, aiscn, flags, ainode->mChildren[i]);
277 node->add_child(child);
281 ldata->node_by_name[node->get_name()] = node;
285 static Mesh *load_mesh(Scene *scn, const aiScene *aiscn, unsigned int flags, const aiMesh *aimesh)
287 Mesh *mesh = new Mesh;
288 mesh->set_name(aimesh->mName.data);
290 int num_verts = aimesh->mNumVertices;
291 int num_faces = aimesh->mNumFaces;
293 mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, (float*)aimesh->mVertices);
295 if(aimesh->mNormals) {
296 mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, (float*)aimesh->mNormals);
298 if(aimesh->mTangents) {
299 mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, (float*)aimesh->mTangents);
301 if(aimesh->mTextureCoords[0]) {
302 mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 3, num_verts, (float*)aimesh->mTextureCoords[0]);
304 if(aimesh->mTextureCoords[1]) {
305 mesh->set_attrib_data(MESH_ATTR_TEXCOORD2, 3, num_verts, (float*)aimesh->mTextureCoords[1]);
308 if(flags & SCNLOAD_FLIPYZ) {
309 Vec3 *vptr = (Vec3*)mesh->get_attrib_data(MESH_ATTR_VERTEX);
310 for(int i=0; i<num_verts; i++) {
315 Vec3 *nptr = (Vec3*)mesh->get_attrib_data(MESH_ATTR_NORMAL);
316 for(int i=0; i<num_verts; i++) {
321 Vec3 *tptr = (Vec3*)mesh->get_attrib_data(MESH_ATTR_TANGENT);
322 for(int i=0; i<num_verts; i++) {
328 unsigned int *iptr = mesh->set_index_data(num_faces * 3);
329 for(int i=0; i<num_faces; i++) {
330 iptr[0] = aimesh->mFaces[i].mIndices[0];
331 iptr[1] = aimesh->mFaces[i].mIndices[flags & SCNLOAD_FLIPYZ ? 2 : 1];
332 iptr[2] = aimesh->mFaces[i].mIndices[flags & SCNLOAD_FLIPYZ ? 1 : 2];
338 static int assimp_textype(aiTextureType type)
341 case aiTextureType_DIFFUSE:
342 return MTL_TEX_DIFFUSE;
343 case aiTextureType_SPECULAR:
344 return MTL_TEX_SPECULAR;
345 case aiTextureType_NORMALS:
346 return MTL_TEX_NORMALMAP;
347 case aiTextureType_LIGHTMAP:
348 case aiTextureType_EMISSIVE:
349 return MTL_TEX_LIGHTMAP;
350 case aiTextureType_REFLECTION:
351 return MTL_TEX_REFLECT;
355 return MTL_TEX_UNKNOWN;
358 /*static const char *assimp_textypestr(aiTextureType type)
361 case aiTextureType_DIFFUSE:
363 case aiTextureType_SPECULAR:
365 case aiTextureType_NORMALS:
367 case aiTextureType_LIGHTMAP:
368 case aiTextureType_EMISSIVE:
370 case aiTextureType_REFLECTION:
378 static Mat4 assimp_matrix(const aiMatrix4x4 &aim)
381 memcpy(m[0], &aim, 16 * sizeof(float));
389 : DataSet<Scene*>(create_scene, load_scene, done_scene, free_scene)
393 Scene *SceneSet::create_scene()
398 bool SceneSet::load_scene(Scene *scn, const char *fname)
400 return scn->load(fname, SCNLOAD_FLIPTEX | SCNLOAD_STAGE_IO);
403 bool SceneSet::done_scene(Scene *scn)
405 bool res = scn->load(0, SCNLOAD_STAGE_GL);
407 scn->metascn->scene_loaded(scn);
412 void SceneSet::free_scene(Scene *scn)
418 // ------ custom file I/O for assimp -------
420 static aiFile *io_open(aiFileIO *io, const char *fname, const char *mode)
423 if(!(fp = ass_fopen(fname, mode))) {
424 error_log("failed to open scene file: %s: %s\n", fname, strerror(ass_errno));
428 aiFile *aifp = new aiFile;
429 aifp->ReadProc = io_read;
431 aifp->TellProc = io_tell;
432 aifp->FileSizeProc = io_filesize;
433 aifp->SeekProc = io_seek;
435 aifp->UserData = (aiUserData)fp;
439 static void io_close(aiFileIO *io, aiFile *aifp)
441 ass_fclose(aifp->UserData);
445 static size_t io_read(aiFile *aifp, char *buf, size_t size, size_t count)
447 return ass_fread(buf, size, count, aifp->UserData);
450 static size_t io_tell(aiFile *aifp)
452 return ass_ftell(aifp->UserData);
455 static size_t io_filesize(aiFile *aifp)
457 ass_file *fp = aifp->UserData;
458 long cur = ass_ftell(fp);
459 ass_fseek(fp, 0, SEEK_END);
460 long off = ass_ftell(fp);
461 ass_fseek(fp, cur, SEEK_SET);
465 static aiReturn io_seek(aiFile *aifp, size_t offs, aiOrigin whence)
467 if(ass_fseek(aifp->UserData, offs, (int)whence) == -1) {
468 return aiReturn_FAILURE;
470 return aiReturn_SUCCESS;