assman was renamed to assfile
[laserbrain_demo] / src / sceneload.cc
index d173538..a488385 100644 (file)
@@ -1,9 +1,11 @@
 #include <stdio.h>
 #include <assert.h>
+#include <string>
 #include <vector>
 #include <map>
 #include <gmath/gmath.h>
 #include <assimp/cimport.h>
+#include <assimp/cfileio.h>
 #include <assimp/postprocess.h>
 #include <assimp/scene.h>
 #include <assimp/mesh.h>
 #include <assimp/vector3.h>
 #include <assimp/matrix4x4.h>
 #include <assimp/quaternion.h>
+#include "assfile.h"
+#include "app.h"
 #include "scene.h"
 #include "objmesh.h"
 #include "datamap.h"
+#include "logger.h"
+#include "metascene.h"
 
 static bool load_material(Scene *scn, Material *mat, const aiMaterial *aimat);
 static SceneNode *load_node(Scene *scn, const aiScene *aiscn, unsigned int flags, const aiNode *ainode);
 static Mesh *load_mesh(Scene *scn, const aiScene *aiscn, unsigned int flags, const aiMesh *aimesh);
-static void print_nodes(SceneNode *node, int lvl = 0);
 /*static const char *mprop_semantic(int x);
 static int count_textures(const aiMaterial *aimat);*/
 static int assimp_textype(aiTextureType type);
-static const char *assimp_textypestr(aiTextureType type);
+//static const char *assimp_textypestr(aiTextureType type);
 
 static Mat4 assimp_matrix(const aiMatrix4x4 &aim);
 
@@ -33,90 +38,134 @@ static long assimp_time(const aiAnimation *anim, double aitime);
 static void print_hierarchy(const aiNode *node);
 */
 
-static std::map<std::string, SceneNode*> node_by_name;
-static std::map<aiMesh*, Mesh*> mesh_by_aimesh;
+static aiFile *io_open(aiFileIO *io, const char *fname, const char *mode);
+static void io_close(aiFileIO *io, aiFile *aifp);
+static size_t io_read(aiFile *aifp, char *buf, size_t size, size_t count);
+static size_t io_tell(aiFile *aifp);
+static size_t io_filesize(aiFile *aifp);
+static aiReturn io_seek(aiFile *aifp, size_t offs, aiOrigin whence);
+
+struct LoaderData {
+       const aiScene *aiscn;
+       std::string fname;
+       std::map<std::string, SceneNode*> node_by_name;
+       std::map<aiMesh*, Mesh*> mesh_by_aimesh;
+};
+
+#define LD_STAGE_MASK  0xf000
 
 bool Scene::load(const char *fname, unsigned int flags)
 {
-       unsigned int ppflags = aiProcess_CalcTangentSpace |
-               aiProcess_GenNormals |
-               aiProcess_JoinIdenticalVertices |
-               aiProcess_Triangulate |
-               aiProcess_SortByPType |
-               aiProcess_GenUVCoords |
-               //aiProcess_PreTransformVertices |
-               aiProcess_TransformUVCoords;
-
-       if(flags & SCNLOAD_FLIPTEX) {
-               ppflags |= aiProcess_FlipUVs;
+       if((flags & LD_STAGE_MASK) == 0) {
+               // not passing either of the stage specifiers, means do the whole job
+               flags |= LD_STAGE_MASK;
        }
 
-       printf("Loading scene file: %s\n", fname);
+       // first perform I/O and all operations not requiring access to an OpenGL context
+       if(flags & SCNLOAD_STAGE_IO) {
+               unsigned int ppflags = aiProcess_CalcTangentSpace |
+                       aiProcess_GenNormals |
+                       aiProcess_JoinIdenticalVertices |
+                       aiProcess_Triangulate |
+                       aiProcess_SortByPType |
+                       aiProcess_GenUVCoords |
+                       //aiProcess_PreTransformVertices |
+                       aiProcess_TransformUVCoords;
+
+               if(flags & SCNLOAD_FLIPTEX) {
+                       ppflags |= aiProcess_FlipUVs;
+               }
 
-       const aiScene *aiscn = aiImportFile(fname, ppflags);
-       if(!aiscn) {
-               fprintf(stderr, "failed to load scene file: %s\n", fname);
-               return false;
-       }
+               info_log("Loading scene file: %s\n", fname);
+               if(this->name.empty()) {
+                       this->name = std::string(fname);
+               }
 
-       // assimp adds its own root node, which might have transformations
-       Vec3 root_pos, root_scaling(1.0, 1.0, 1.0);
-       Quat root_rot;
+               aiFileIO io;
+               io.OpenProc = io_open;
+               io.CloseProc = io_close;
 
-       if(aiscn->mRootNode) {
-               Mat4 root_matrix = assimp_matrix(aiscn->mRootNode->mTransformation);
-               root_pos = root_matrix.get_translation();
-               root_rot = root_matrix.get_rotation();
-               root_scaling = root_matrix.get_scaling();
+               const aiScene *aiscn = aiImportFileEx(fname, ppflags, &io);
+               if(!aiscn) {
+                       error_log("failed to load scene file: %s\n", fname);
+                       return false;
+               }
 
-               printf("assimp root node: %s\n", aiscn->mRootNode->mName.data);
-               printf("  pos: %f %f %f\n", root_pos.x, root_pos.y, root_pos.z);
-               printf("  rot: %f %+f %+f %+f\n", root_rot.w, root_rot.x, root_rot.y, root_rot.z);
-               printf("  scaling: %f %f %f\n", root_scaling.x, root_scaling.y, root_scaling.z);
+               LoaderData *ldata = new LoaderData;
+               ldata->aiscn = aiscn;
+               ldata->fname = std::string(fname);
+               loader_data = (void*)ldata;
        }
 
-       // load all meshes
-       for(unsigned int i=0; i<aiscn->mNumMeshes; i++) {
-               aiMesh *aimesh = aiscn->mMeshes[i];
-               Mesh *mesh;
+       /* then, assuming we have successfully loaded everything, proceed to construct
+        * all the engine objects, which require access to the OpenGL context
+        */
+       if(flags & SCNLOAD_STAGE_GL) {
+               if(!loader_data) {
+                       error_log("second stage scene loader failed to find valid I/O data\n");
+                       return false;
+               }
 
-               switch(aimesh->mPrimitiveTypes) {
-               case aiPrimitiveType_TRIANGLE:
-                       if((mesh = load_mesh(this, aiscn, flags, aimesh))) {
-                               mesh_by_aimesh[aimesh] = mesh;
-                               meshes.push_back(mesh);
-                       }
-                       break;
+               LoaderData *ldata = (LoaderData*)loader_data;
+               const aiScene *aiscn = ldata->aiscn;
+               fname = ldata->fname.c_str();
+
+               clear();        // clear any previous data (TODO: add flag for not clearing)
+
+               // assimp adds its own root node, which might have transformations
+               Vec3 root_pos, root_scaling(1.0, 1.0, 1.0);
+               Quat root_rot;
 
-               default:
-                       fprintf(stderr, "unsupported primitive type: %u\n", aimesh->mPrimitiveTypes);
-                       break;
+               if(aiscn->mRootNode) {
+                       Mat4 root_matrix = assimp_matrix(aiscn->mRootNode->mTransformation);
+                       root_pos = root_matrix.get_translation();
+                       root_rot = root_matrix.get_rotation();
+                       root_scaling = root_matrix.get_scaling();
                }
-       }
 
-       if(!nodes) {
-               nodes = new SceneNode;
-               nodes->set_name("root");
-               nodes->set_position(root_pos);
-               nodes->set_rotation(root_rot);
-               nodes->set_scaling(root_scaling);
-       }
+               if(!nodes) {
+                       nodes = new SceneNode;
+                       nodes->scene = this;
+                       nodes->set_name("root");
+                       nodes->set_position(root_pos);
+                       nodes->set_rotation(root_rot);
+                       nodes->set_scaling(root_scaling);
+               }
 
-       // load all the nodes recursively
-       for(unsigned int i=0; i<aiscn->mRootNode->mNumChildren; i++) {
-               SceneNode *node = load_node(this, aiscn, flags, aiscn->mRootNode->mChildren[i]);
-               if(node) {
-                       nodes->add_child(node);
+               // load all meshes
+               for(unsigned int i=0; i<aiscn->mNumMeshes; i++) {
+                       aiMesh *aimesh = aiscn->mMeshes[i];
+                       Mesh *mesh;
+
+                       switch(aimesh->mPrimitiveTypes) {
+                       case aiPrimitiveType_TRIANGLE:
+                               if((mesh = load_mesh(this, aiscn, flags, aimesh))) {
+                                       ldata->mesh_by_aimesh[aimesh] = mesh;
+                                       meshes.push_back(mesh);
+                               }
+                               break;
+
+                       default:
+                               error_log("unsupported primitive type: %u\n", aimesh->mPrimitiveTypes);
+                               break;
+                       }
                }
-       }
 
-       node_by_name.clear();
-       mesh_by_aimesh.clear();
+               // load all the nodes recursively
+               for(unsigned int i=0; i<aiscn->mRootNode->mNumChildren; i++) {
+                       SceneNode *node = load_node(this, aiscn, flags, aiscn->mRootNode->mChildren[i]);
+                       if(node) {
+                               nodes->add_child(node);
+                       }
+               }
+
+               info_log("loaded scene file: %s, %d meshes\n", fname, (int)meshes.size());
 
-       aiReleaseImport(aiscn);
-       printf("loaded scene file: %s, %d meshes\n", fname, (int)meshes.size());
-       nodes->update(0);
-       print_nodes(nodes);
+               aiReleaseImport(aiscn);
+               delete ldata;
+               loader_data = 0;
+               nodes->update(0);
+       }
        return true;
 }
 
@@ -131,7 +180,7 @@ static bool load_material(Scene *scn, Material *mat, const aiMaterial *aimat)
        } else {
                mat->name = "unknown";
        }
-       printf("load_material: %s\n", mat->name.c_str());
+       //info_log("load_material: %s\n", mat->name.c_str());
 
        if(aiGetMaterialColor(aimat, AI_MATKEY_COLOR_DIFFUSE, &aicol) == 0) {
                mat->diffuse = Vec3(aicol[0], aicol[1], aicol[2]);
@@ -162,29 +211,21 @@ static bool load_material(Scene *scn, Material *mat, const aiMaterial *aimat)
                                continue;
                        }
 
-                       char *fname;
-                       int nsize = datamap_path_size(aipath.data);
-                       if(nsize) {
-                               fname = new char[nsize];
-                               datamap_lookup(aipath.data, fname, nsize);
-                       } else {
-                               fname = new char[strlen(aipath.data) + 1];
-                               char *dptr = fname;
-                               char *sptr = aipath.data;
-                               do {
-                                       *dptr++ = *sptr == '\\' ? '/' : *sptr;
-                               } while(*sptr++);
-                       }
+                       char *fname = (char*)alloca(strlen(aipath.data) + 1);
+                       char *dptr = fname;
+                       char *sptr = aipath.data;
+                       do {
+                               *dptr++ = *sptr == '\\' ? '/' : *sptr;
+                       } while(*sptr++);
+
+                       if(!fname || !*fname) continue;
 
                        int textype = assimp_textype(aitype);
-                       printf("loading %s texture: %s\n", assimp_textypestr(aitype), fname);
 
-                       Texture *tex = scn->texset->get_texture(fname, TEX_2D);
+                       Texture *tex = texman.get_texture(fname, TEX_2D, &scn->datamap);
                        assert(tex);
                        mat->textures.push_back(tex);
 
-                       printf("   DBG(%p)\n", (void*)tex);
-
                        if(textype != MTL_TEX_UNKNOWN && !mat->stdtex[textype]) {
                                mat->stdtex[textype] = tex;
                        }
@@ -196,6 +237,8 @@ static bool load_material(Scene *scn, Material *mat, const aiMaterial *aimat)
 
 static SceneNode *load_node(Scene *scn, const aiScene *aiscn, unsigned int flags, const aiNode *ainode)
 {
+       LoaderData *ldata = (LoaderData*)scn->loader_data;
+
        SceneNode *node = new SceneNode;
        node->set_name(ainode->mName.data);
 
@@ -214,9 +257,10 @@ static SceneNode *load_node(Scene *scn, const aiScene *aiscn, unsigned int flags
        for(unsigned int i=0; i<ainode->mNumMeshes; i++) {
                aiMesh *aimesh = aiscn->mMeshes[ainode->mMeshes[i]];
 
-               Mesh *mesh = mesh_by_aimesh[aimesh];
+               Mesh *mesh = ldata->mesh_by_aimesh[aimesh];
                if(mesh) {
                        ObjMesh *obj = new ObjMesh;
+                       obj->set_name(mesh->get_name());
                        obj->mesh = mesh;
                        // also grab the material of this mesh
                        load_material(scn, &obj->mtl, aiscn->mMaterials[aimesh->mMaterialIndex]);
@@ -234,13 +278,14 @@ static SceneNode *load_node(Scene *scn, const aiScene *aiscn, unsigned int flags
                }
        }
 
-       node_by_name[node->get_name()] = node;
+       ldata->node_by_name[node->get_name()] = node;
        return node;
 }
 
 static Mesh *load_mesh(Scene *scn, const aiScene *aiscn, unsigned int flags, const aiMesh *aimesh)
 {
        Mesh *mesh = new Mesh;
+       mesh->set_name(aimesh->mName.data);
 
        int num_verts = aimesh->mNumVertices;
        int num_faces = aimesh->mNumFaces;
@@ -287,39 +332,9 @@ static Mesh *load_mesh(Scene *scn, const aiScene *aiscn, unsigned int flags, con
                iptr[2] = aimesh->mFaces[i].mIndices[flags & SCNLOAD_FLIPYZ ? 1 : 2];
                iptr += 3;
        }
-
-       AABox bbox = mesh->get_aabbox();
-       printf("mesh bounding box: %f %f %f -> %f %f %f\n", bbox.min.x, bbox.min.y, bbox.min.z,
-                       bbox.max.x, bbox.max.y, bbox.max.z);
-
        return mesh;
 }
 
-static void print_nodes(SceneNode *node, int lvl)
-{
-       Vec3 pos = node->get_node_position();
-       Quat rot = node->get_node_rotation();
-       Vec3 scale = node->get_node_scaling();
-
-       const char *type = node->get_num_objects() > 0 ? "mesh node" : "null node";
-
-       for(int i=0; i<lvl; i++) {
-               fputs("  ", stdout);
-       }
-       printf("%s[%s] p(%g %g %g) rq(%g %+gi %+gj %+gk) s(%g %g %g)\n", type, node->get_name(),
-                       pos.x, pos.y, pos.z, rot.w, rot.x, rot.y, rot.z, scale.x, scale.y, scale.z);
-
-       if(node->get_num_objects()) {
-               Mat4 xform = node->get_matrix();
-               xform.print(stdout);
-       }
-
-       int nchld = node->get_num_children();
-       for(int i=0; i<nchld; i++) {
-               print_nodes(node->get_child(i), lvl + 1);
-       }
-}
-
 static int assimp_textype(aiTextureType type)
 {
        switch(type) {
@@ -333,14 +348,14 @@ static int assimp_textype(aiTextureType type)
        case aiTextureType_EMISSIVE:
                return MTL_TEX_LIGHTMAP;
        case aiTextureType_REFLECTION:
-               return MTL_TEX_ENVMAP;
+               return MTL_TEX_REFLECT;
        default:
                break;
        }
        return MTL_TEX_UNKNOWN;
 }
 
-static const char *assimp_textypestr(aiTextureType type)
+/*static const char *assimp_textypestr(aiTextureType type)
 {
        switch(type) {
        case aiTextureType_DIFFUSE:
@@ -358,7 +373,7 @@ static const char *assimp_textypestr(aiTextureType type)
                break;
        }
        return "unknown";
-}
+}*/
 
 static Mat4 assimp_matrix(const aiMatrix4x4 &aim)
 {
@@ -366,3 +381,91 @@ static Mat4 assimp_matrix(const aiMatrix4x4 &aim)
        memcpy(m[0], &aim, 16 * sizeof(float));
        return transpose(m);
 }
+
+
+// --- SceneSet ---
+
+SceneSet::SceneSet()
+       : DataSet<Scene*>(create_scene, load_scene, done_scene, free_scene)
+{
+}
+
+Scene *SceneSet::create_scene()
+{
+       return new Scene;
+}
+
+bool SceneSet::load_scene(Scene *scn, const char *fname)
+{
+       return scn->load(fname, SCNLOAD_FLIPTEX | SCNLOAD_STAGE_IO);
+}
+
+bool SceneSet::done_scene(Scene *scn)
+{
+       bool res = scn->load(0, SCNLOAD_STAGE_GL);
+       if(scn->metascn) {
+               scn->metascn->scene_loaded(scn);
+       }
+       return res;
+}
+
+void SceneSet::free_scene(Scene *scn)
+{
+       delete scn;
+}
+
+
+// ------ custom file I/O for assimp -------
+
+static aiFile *io_open(aiFileIO *io, const char *fname, const char *mode)
+{
+       ass_file *fp;
+       if(!(fp = ass_fopen(fname, mode))) {
+               error_log("failed to open scene file: %s: %s\n", fname, strerror(ass_errno));
+               return 0;
+       }
+
+       aiFile *aifp = new aiFile;
+       aifp->ReadProc = io_read;
+       aifp->WriteProc = 0;
+       aifp->TellProc = io_tell;
+       aifp->FileSizeProc = io_filesize;
+       aifp->SeekProc = io_seek;
+       aifp->FlushProc = 0;
+       aifp->UserData = (aiUserData)fp;
+       return aifp;
+}
+
+static void io_close(aiFileIO *io, aiFile *aifp)
+{
+       ass_fclose(aifp->UserData);
+       delete aifp;
+}
+
+static size_t io_read(aiFile *aifp, char *buf, size_t size, size_t count)
+{
+       return ass_fread(buf, size, count, aifp->UserData);
+}
+
+static size_t io_tell(aiFile *aifp)
+{
+       return ass_ftell(aifp->UserData);
+}
+
+static size_t io_filesize(aiFile *aifp)
+{
+       ass_file *fp = aifp->UserData;
+       long cur = ass_ftell(fp);
+       ass_fseek(fp, 0, SEEK_END);
+       long off = ass_ftell(fp);
+       ass_fseek(fp, cur, SEEK_SET);
+       return off;
+}
+
+static aiReturn io_seek(aiFile *aifp, size_t offs, aiOrigin whence)
+{
+       if(ass_fseek(aifp->UserData, offs, (int)whence) == -1) {
+               return aiReturn_FAILURE;
+       }
+       return aiReturn_SUCCESS;
+}