#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 "assman.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);
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;
}
} 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]);
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;
}
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);
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]);
}
}
- 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;
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) {
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:
break;
}
return "unknown";
-}
+}*/
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;
+}