+#include <stdio.h>
+#include <vector>
+#include <map>
+#include <gmath/gmath.h>
+#include <assimp/cimport.h>
+#include <assimp/postprocess.h>
+#include <assimp/scene.h>
+#include <assimp/mesh.h>
+#include <assimp/material.h>
+#include <assimp/anim.h>
+#include <assimp/vector3.h>
+#include <assimp/matrix4x4.h>
+#include <assimp/quaternion.h>
+#include "scene.h"
+#include "objmesh.h"
+
+static bool load_material(Material *mat, const aiMaterial *aimat);
+static SceneNode *load_node(const aiScene *aiscn, const aiNode *ainode);
+static Mesh *load_mesh(const aiScene *aiscn, const aiMesh *aimesh);
+
+/*static Vec3 assimp_vector(const aiVector3D &v);
+static Quat assimp_quat(const aiQuaternion &q);
+static Mat4 assimp_matrix(const aiMatrix4x4 &aim);
+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;
+
+bool Scene::load(const char *fname)
+{
+ unsigned int ppflags = aiProcess_CalcTangentSpace |
+ aiProcess_GenNormals |
+ aiProcess_JoinIdenticalVertices |
+ aiProcess_Triangulate |
+ aiProcess_SortByPType |
+ aiProcess_TransformUVCoords;
+
+ const aiScene *aiscn = aiImportFile(fname, ppflags);
+ if(!aiscn) {
+ fprintf(stderr, "failed to load scene file: %s\n", fname);
+ return false;
+ }
+
+ /*
+ Vec3 root_pos, root_scaling(1.0, 1.0, 1.0);
+ Quat root_rot;
+
+ if(aiscn->mRootNode) {
+ Mat4 root_matrix = assimp_matrix(aiscn->mRootNode->mTransformation);
+ root_pos = root_matrix.get_translation();
+ root_rot = root_matrix.get_rotation_quat();
+ root_scaling = root_matrix.get_scaling();
+ }*/
+
+ // 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(aiscn, aimesh))) {
+ mesh_by_aimesh[aimesh] = mesh;
+ meshes.push_back(mesh);
+ }
+ break;
+
+ default:
+ fprintf(stderr, "unsupported primitive type: %u\n", aimesh->mPrimitiveTypes);
+ break;
+ }
+ }
+
+ SceneNode *root = new SceneNode;
+
+ // load all the nodes recursively
+ for(unsigned int i=0; i<aiscn->mRootNode->mNumChildren; i++) {
+ SceneNode *node = load_node(aiscn, aiscn->mRootNode->mChildren[i]);
+ if(node) {
+ root->add_child(node);
+ }
+ }
+
+ int nnodes = root->get_num_children();
+ if(nnodes <= 0) {
+ delete root;
+ } else if(nnodes == 1) {
+ nodes = root->get_child(0);
+ root->remove_child(nodes);
+ delete root;
+ } else {
+ nodes = root;
+ }
+
+ node_by_name.clear();
+ mesh_by_aimesh.clear();
+
+ aiReleaseImport(aiscn);
+ printf("loaded scene file: %s, %d meshes\n", fname, (int)meshes.size());
+ return true;
+}
+
+static bool load_material(Material *mat, const aiMaterial *aimat)
+{
+ aiColor4D aicol;
+ float shin, shin_str;
+
+ if(aiGetMaterialColor(aimat, AI_MATKEY_COLOR_DIFFUSE, &aicol) == 0) {
+ mat->diffuse = Vec3(aicol[0], aicol[1], aicol[2]);
+ }
+ if(aiGetMaterialColor(aimat, AI_MATKEY_COLOR_SPECULAR, &aicol) == 0) {
+ mat->specular = Vec3(aicol[0], aicol[1], aicol[2]);
+ }
+
+ unsigned int count = 1;
+ if(aiGetMaterialFloatArray(aimat, AI_MATKEY_SHININESS_STRENGTH, &shin_str, &count) != 0) {
+ shin_str = 1.0;
+ }
+ if(aiGetMaterialFloatArray(aimat, AI_MATKEY_SHININESS, &shin, &count) == 0) {
+ // XXX can't remember how I came up with this...
+ mat->shininess = shin * shin_str * 0.0001 * 128.0;
+ }
+
+ /*
+ // load textures
+ struct { int type; aiTextureType aitype; } textypes[] = {
+ {TEX_DIFFUSE, aiTextureType_DIFFUSE},
+ {TEX_NORMAL, aiTextureType_NORMALS},
+ {TEX_SPECULAR, aiTextureType_SPECULAR}
+ };
+
+ for(size_t i=0; i<sizeof textypes / sizeof *textypes; i++) {
+ aiString aipath;
+
+ if(aiGetMaterialTexture(aimat, textypes[i].aitype, 0, &aipath) == 0) {
+ char *tmp, *fname = aipath.data;
+
+ if((tmp = strrchr(fname, '/'))) {
+ fname = tmp + 1;
+ }
+ if((tmp = strrchr(fname, '\\'))) {
+ fname = tmp + 1;
+ }
+
+ if(*fname) {
+ mat->tex[textypes[i].type] = texset.get(fname);
+ }
+ }
+ }
+ */
+
+ return true;
+}
+
+static SceneNode *load_node(const aiScene *aiscn, const aiNode *ainode)
+{
+ 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[0]];
+
+ Mesh *mesh = mesh_by_aimesh[aimesh];
+ if(mesh) {
+ ObjMesh *obj = new ObjMesh;
+ obj->mesh = mesh;
+ // also grab the material of this mesh
+ load_material(&obj->mtl, aiscn->mMaterials[aimesh->mMaterialIndex]);
+
+ node->add_object(obj);
+ }
+ }
+
+ /* recurse to all children */
+ for(unsigned int i=0; i<ainode->mNumChildren; i++) {
+ SceneNode *child = load_node(aiscn, ainode->mChildren[i]);
+ if(child) {
+ node->add_child(child);
+ }
+ }
+
+ node_by_name[node->get_name()] = node;
+ return node;
+}
+
+static Mesh *load_mesh(const aiScene *aiscn, const aiMesh *aimesh)
+{
+ Mesh *mesh = new Mesh;
+
+ int num_verts = aimesh->mNumVertices;
+ int num_faces = aimesh->mNumFaces;
+
+ mesh->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, (float*)aimesh->mVertices);
+
+ if(aimesh->mNormals) {
+ mesh->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, (float*)aimesh->mNormals);
+ }
+ if(aimesh->mTangents) {
+ mesh->set_attrib_data(MESH_ATTR_TANGENT, 3, num_verts, (float*)aimesh->mTangents);
+ }
+ if(aimesh->mTextureCoords[0]) {
+ mesh->set_attrib_data(MESH_ATTR_TEXCOORD, 3, num_verts, (float*)aimesh->mTextureCoords[0]);
+ }
+
+ unsigned int *iptr = mesh->set_index_data(num_faces * 3);
+ for(int i=0; i<num_faces; i++) {
+ for(int j=0; j<3; j++) {
+ *iptr++ = aimesh->mFaces[i].mIndices[j];
+ }
+ }
+
+ return mesh;
+}
+
+#if 0
+static Vec3 assimp_vector(const aiVector3D &v)
+{
+ return Vec3(v[0], v[1], v[2]);
+}
+
+static Quat assimp_quat(const aiQuaternion &q)
+{
+ return Quat(q.x, q.y, q.z, q.w);
+}
+
+static Mat4 assimp_matrix(const aiMatrix4x4 &aim)
+{
+ Mat4 m;
+ memcpy(m[0], &aim, 16 * sizeof(float));
+ m.transpose();
+ return m;
+}
+
+/* convert an assimp keyframe time (ticks) into milliseconds */
+static long assimp_time(const aiAnimation *anim, double aitime)
+{
+ double sec;
+ if(anim->mTicksPerSecond < 1e-6) {
+ // assume time is in frames?
+ sec = aitime / 30.0;
+ } else {
+ sec = aitime / anim->mTicksPerSecond;
+ }
+ return (long)(sec * 1000.0);
+}
+
+static void print_hierarchy(const aiNode *node)
+{
+ static int lvl;
+ static int lvlopen[256];
+
+ for(int i=0; i<lvl; i++) {
+ putchar(' ');
+ if(lvlopen[i]) {
+ putchar(i >= lvl - 1 ? '+' : '|');
+ } else {
+ putchar(i >= lvl - 1 ? '+' : ' ');
+ }
+ }
+ printf("- \"%s\"\n", node->mName.data);
+
+ lvlopen[lvl] = 1;
+
+ lvl++;
+ for(unsigned int i=0; i<node->mNumChildren; i++) {
+ if(i == node->mNumChildren - 1) {
+ lvlopen[lvl - 1] = 0;
+ }
+ print_hierarchy(node->mChildren[i]);
+ }
+ lvl--;
+}
+#endif /* 0 */