nuclear@29: #include nuclear@29: #include nuclear@29: #include "goat3d.h" nuclear@29: #include "assimp/cimport.h" nuclear@29: #include "assimp/postprocess.h" nuclear@29: #include "assimp/scene.h" nuclear@29: nuclear@55: enum { nuclear@55: CONV_SCENE, nuclear@55: CONV_ANIM nuclear@55: }; nuclear@55: nuclear@55: int convert(const char *infname); nuclear@55: int convert_anim(const char *infname); nuclear@29: void process_material(struct goat3d_material *mtl, struct aiMaterial *aimtl); nuclear@57: void process_mesh(struct goat3d *goat, struct goat3d_mesh *mesh, struct aiMesh *aimesh); nuclear@29: void process_node(struct goat3d *goat, struct goat3d_node *parent, struct aiNode *ainode); nuclear@55: int process_anim(struct goat3d *goat, struct aiAnimation *aianim); nuclear@55: static int output_filename(char *buf, int bufsz, const char *fname, const char *suffix); nuclear@55: static long assimp_time(const struct aiAnimation *anim, double aitime); nuclear@29: nuclear@29: int main(int argc, char **argv) nuclear@29: { nuclear@29: int i, num_done = 0; nuclear@55: int conv_targ = CONV_SCENE; nuclear@29: nuclear@29: for(i=1; i %s\n", infname, outfname); nuclear@55: nuclear@55: nuclear@55: if(!(aiscn = aiImportFile(infname, SCE_PPFLAGS))) { nuclear@29: fprintf(stderr, "failed to import %s\n", infname); nuclear@29: return -1; nuclear@29: } nuclear@29: nuclear@29: goat = goat3d_create(); nuclear@29: nuclear@29: for(i=0; i<(int)aiscn->mNumMaterials; i++) { nuclear@29: struct aiMaterial *aimat = aiscn->mMaterials[i]; nuclear@29: struct goat3d_material *mat = goat3d_create_mtl(); nuclear@29: nuclear@29: process_material(mat, aimat); nuclear@29: goat3d_add_mtl(goat, mat); nuclear@29: } nuclear@29: nuclear@57: for(i=0; i<(int)aiscn->mNumMeshes; i++) { nuclear@57: struct aiMesh *aimesh = aiscn->mMeshes[i]; nuclear@57: struct goat3d_mesh *mesh = goat3d_create_mesh(); nuclear@57: nuclear@57: process_mesh(goat, mesh, aimesh); nuclear@57: goat3d_add_mesh(goat, mesh); nuclear@57: } nuclear@57: nuclear@29: for(i=0; i<(int)aiscn->mRootNode->mNumChildren; i++) { nuclear@29: process_node(goat, 0, aiscn->mRootNode->mChildren[i]); nuclear@29: } nuclear@29: nuclear@29: goat3d_save(goat, outfname); nuclear@29: goat3d_free(goat); nuclear@29: aiReleaseImport(aiscn); nuclear@29: return 0; nuclear@29: } nuclear@29: nuclear@55: int convert_anim(const char *infname) nuclear@55: { nuclear@55: int i, bufsz; nuclear@55: const struct aiScene *aiscn; nuclear@55: struct goat3d *goat; nuclear@55: char *outfname; nuclear@55: nuclear@55: bufsz = output_filename(0, 0, infname, "goatanim"); nuclear@55: outfname = alloca(bufsz); nuclear@55: output_filename(outfname, bufsz, infname, "goatanim"); nuclear@55: printf("converting %s -> %s\n", infname, outfname); nuclear@55: nuclear@55: nuclear@55: if(!(aiscn = aiImportFile(infname, ANM_PPFLAGS))) { nuclear@55: fprintf(stderr, "failed to import %s\n", infname); nuclear@55: return -1; nuclear@55: } nuclear@55: nuclear@55: goat = goat3d_create(); nuclear@55: nuclear@55: for(i=0; i<(int)aiscn->mRootNode->mNumChildren; i++) { nuclear@55: process_node(goat, 0, aiscn->mRootNode->mChildren[i]); nuclear@55: } nuclear@55: nuclear@55: for(i=0; imNumAnimations; i++) { nuclear@55: if(process_anim(goat, aiscn->mAnimations[i]) == -1) { nuclear@55: return -1; nuclear@55: } nuclear@55: } nuclear@55: nuclear@55: goat3d_save_anim(goat, outfname); nuclear@55: goat3d_free(goat); nuclear@55: aiReleaseImport(aiscn); nuclear@55: return 0; nuclear@55: } nuclear@55: nuclear@29: void process_material(struct goat3d_material *mtl, struct aiMaterial *aimtl) nuclear@29: { nuclear@29: struct aiString aistr; nuclear@29: struct aiColor4D color; nuclear@29: float val; nuclear@29: nuclear@29: if(aiGetMaterialString(aimtl, AI_MATKEY_NAME, &aistr) == aiReturn_SUCCESS) { nuclear@29: goat3d_set_mtl_name(mtl, aistr.data); nuclear@29: } nuclear@29: nuclear@29: if(aiGetMaterialColor(aimtl, AI_MATKEY_COLOR_DIFFUSE, &color) == aiReturn_SUCCESS) { nuclear@29: goat3d_set_mtl_attrib3f(mtl, GOAT3D_MAT_ATTR_DIFFUSE, color.r, color.g, color.b); nuclear@29: } nuclear@29: nuclear@29: if(aiGetMaterialColor(aimtl, AI_MATKEY_COLOR_SPECULAR, &color) == aiReturn_SUCCESS) { nuclear@29: float sstr = 1.0; nuclear@29: aiGetMaterialFloatArray(aimtl, AI_MATKEY_SHININESS_STRENGTH, &sstr, 0); nuclear@29: goat3d_set_mtl_attrib3f(mtl, GOAT3D_MAT_ATTR_SPECULAR, color.r * sstr, color.g * sstr, color.b * sstr); nuclear@29: } nuclear@29: nuclear@29: if(aiGetMaterialFloatArray(aimtl, AI_MATKEY_BUMPSCALING, &val, 0) == aiReturn_SUCCESS) { nuclear@29: goat3d_set_mtl_attrib3f(mtl, GOAT3D_MAT_ATTR_BUMP, val, val, val); nuclear@29: } nuclear@29: nuclear@29: if(aiGetMaterialFloatArray(aimtl, AI_MATKEY_REFLECTIVITY, &val, 0) == aiReturn_SUCCESS) { nuclear@29: goat3d_set_mtl_attrib1f(mtl, GOAT3D_MAT_ATTR_REFLECTION, val); nuclear@29: } nuclear@29: nuclear@29: if(aiGetMaterialFloatArray(aimtl, AI_MATKEY_OPACITY, &val, 0) == aiReturn_SUCCESS) { nuclear@29: goat3d_set_mtl_attrib1f(mtl, GOAT3D_MAT_ATTR_TRANSMISSION, 1.0 - val); nuclear@29: } nuclear@29: nuclear@29: if(aiGetMaterialString(aimtl, AI_MATKEY_TEXTURE_DIFFUSE(0), &aistr) == aiReturn_SUCCESS) { nuclear@29: goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_DIFFUSE, aistr.data); nuclear@29: } nuclear@29: if(aiGetMaterialString(aimtl, AI_MATKEY_TEXTURE_SPECULAR(0), &aistr) == aiReturn_SUCCESS) { nuclear@29: goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_SPECULAR, aistr.data); nuclear@29: } nuclear@29: if(aiGetMaterialString(aimtl, AI_MATKEY_TEXTURE_SHININESS(0), &aistr) == aiReturn_SUCCESS) { nuclear@29: goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_SHININESS, aistr.data); nuclear@29: } nuclear@29: if(aiGetMaterialString(aimtl, AI_MATKEY_TEXTURE_NORMALS(0), &aistr) == aiReturn_SUCCESS) { nuclear@29: goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_NORMAL, aistr.data); nuclear@29: } nuclear@29: if(aiGetMaterialString(aimtl, AI_MATKEY_TEXTURE_REFLECTION(0), &aistr) == aiReturn_SUCCESS) { nuclear@29: goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_REFLECTION, aistr.data); nuclear@29: } nuclear@29: if(aiGetMaterialString(aimtl, AI_MATKEY_TEXTURE_OPACITY(0), &aistr) == aiReturn_SUCCESS) { nuclear@57: /* TODO this is semantically inverted... maybe add an alpha attribute? */ nuclear@29: goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_TRANSMISSION, aistr.data); nuclear@29: } nuclear@29: } nuclear@29: nuclear@57: void process_mesh(struct goat3d *goat, struct goat3d_mesh *mesh, struct aiMesh *aimesh) nuclear@57: { nuclear@57: int i, num_verts, num_faces; nuclear@57: struct goat3d_material *mtl; nuclear@57: nuclear@57: if(aimesh->mName.length > 0) { nuclear@57: goat3d_set_mesh_name(mesh, aimesh->mName.data); nuclear@57: } nuclear@57: nuclear@57: if((mtl = goat3d_get_mtl(goat, aimesh->mMaterialIndex))) { nuclear@57: goat3d_set_mesh_mtl(mesh, mtl); nuclear@57: } nuclear@57: nuclear@57: num_verts = aimesh->mNumVertices; nuclear@57: num_faces = aimesh->mNumFaces; nuclear@57: nuclear@57: for(i=0; imVertices + i; nuclear@57: goat3d_add_mesh_attrib3f(mesh, GOAT3D_MESH_ATTR_VERTEX, v->x, v->y, v->z); nuclear@57: nuclear@57: if(aimesh->mNormals) { nuclear@57: v = aimesh->mNormals + i; nuclear@57: goat3d_add_mesh_attrib3f(mesh, GOAT3D_MESH_ATTR_NORMAL, v->x, v->y, v->z); nuclear@57: } nuclear@57: if(aimesh->mTangents) { nuclear@57: v = aimesh->mTangents + i; nuclear@57: goat3d_add_mesh_attrib3f(mesh, GOAT3D_MESH_ATTR_TANGENT, v->x, v->y, v->z); nuclear@57: } nuclear@57: if(aimesh->mTextureCoords[0]) { nuclear@57: v = aimesh->mTextureCoords[0] + i; nuclear@57: goat3d_add_mesh_attrib2f(mesh, GOAT3D_MESH_ATTR_TEXCOORD, v->x, v->y); nuclear@57: } nuclear@57: if(aimesh->mColors[0]) { nuclear@57: col = aimesh->mColors[0] + i; nuclear@57: goat3d_add_mesh_attrib4f(mesh, GOAT3D_MESH_ATTR_COLOR, col->r, col->g, col->b, col->a); nuclear@57: } nuclear@57: /* TODO: add bones */ nuclear@57: } nuclear@57: nuclear@57: for(i=0; imFaces + i; nuclear@57: nuclear@57: goat3d_add_mesh_face(mesh, face->mIndices[0], face->mIndices[1], face->mIndices[2]); nuclear@57: } nuclear@57: } nuclear@57: nuclear@29: void process_node(struct goat3d *goat, struct goat3d_node *parent, struct aiNode *ainode) nuclear@29: { nuclear@29: int i; nuclear@29: struct goat3d_node *node; nuclear@29: nuclear@29: node = goat3d_create_node(); nuclear@29: goat3d_set_node_name(node, ainode->mName.data); nuclear@29: nuclear@29: for(i=0; imNumChildren; i++) { nuclear@29: process_node(goat, node, ainode->mChildren[i]); nuclear@29: } nuclear@29: nuclear@29: goat3d_add_node(goat, node); nuclear@29: } nuclear@55: nuclear@55: int process_anim(struct goat3d *goat, struct aiAnimation *aianim) nuclear@55: { nuclear@55: int i, j, num_nodes, rnodes_count; nuclear@55: const char *anim_name; nuclear@55: nuclear@55: if(aianim->mName.length <= 0) { nuclear@55: anim_name = "unnamed"; nuclear@55: } else { nuclear@55: anim_name = aianim->mName.data; nuclear@55: } nuclear@55: nuclear@55: num_nodes = goat3d_get_node_count(goat); nuclear@55: nuclear@55: rnodes_count = 0; nuclear@55: for(i=0; imNumChannels; i++) { nuclear@55: struct goat3d_node *node; nuclear@55: struct aiNodeAnim *ainodeanim = aianim->mChannels[i]; nuclear@55: nuclear@55: /* find the node it refers to */ nuclear@55: const char *nodename = ainodeanim->mNodeName.data; nuclear@55: if(!(node = goat3d_get_node_by_name(goat, nodename))) { nuclear@55: fprintf(stderr, "failed to process animation for unknown node: %s\n", nodename); nuclear@55: return -1; nuclear@55: } nuclear@55: nuclear@55: /* add all the keys ... */ nuclear@55: for(j=0; j<(int)ainodeanim->mNumPositionKeys; j++) { nuclear@55: struct aiVectorKey *key = ainodeanim->mPositionKeys + j; nuclear@55: long tm = assimp_time(aianim, key->mTime); nuclear@55: goat3d_set_node_position(node, key->mValue.x, key->mValue.y, key->mValue.z, tm); nuclear@55: } nuclear@55: nuclear@55: for(j=0; j<(int)ainodeanim->mNumRotationKeys; j++) { nuclear@55: struct aiQuatKey *key = ainodeanim->mRotationKeys + j; nuclear@55: long tm = assimp_time(aianim, key->mTime); nuclear@55: goat3d_set_node_rotation(node, key->mValue.x, key->mValue.y, key->mValue.z, key->mValue.w, tm); nuclear@55: } nuclear@55: nuclear@55: for(j=0; j<(int)ainodeanim->mNumScalingKeys; j++) { nuclear@55: struct aiVectorKey *key = ainodeanim->mScalingKeys + j; nuclear@55: long tm = assimp_time(aianim, key->mTime); nuclear@55: goat3d_set_node_scaling(node, key->mValue.x, key->mValue.y, key->mValue.z, tm); nuclear@55: } nuclear@55: } nuclear@55: nuclear@55: return 0; nuclear@55: } nuclear@55: nuclear@55: static int output_filename(char *buf, int bufsz, const char *fname, const char *suffix) nuclear@55: { nuclear@55: int reqsz, namesz; nuclear@55: char *tmpfname; nuclear@55: const char *fname_end, *lastdot; nuclear@55: nuclear@55: lastdot = strrchr(fname, '.'); nuclear@55: nuclear@56: fname_end = lastdot ? lastdot : fname + strlen(fname); nuclear@55: namesz = fname_end - fname; nuclear@55: reqsz = namesz + strlen(suffix) + 2; /* plus 1 for the dot */ nuclear@55: nuclear@55: if(buf && bufsz) { nuclear@55: tmpfname = alloca(namesz + 1); nuclear@55: memcpy(tmpfname, fname, namesz); nuclear@55: tmpfname[namesz] = 0; nuclear@55: nuclear@55: if(suffix) { nuclear@55: snprintf(buf, bufsz, "%s.%s", tmpfname, suffix); nuclear@55: } else { nuclear@55: strncpy(buf, tmpfname, bufsz); nuclear@55: } nuclear@55: buf[bufsz - 1] = 0; nuclear@55: } nuclear@55: nuclear@55: return reqsz; nuclear@55: } nuclear@55: nuclear@55: static long assimp_time(const struct aiAnimation *anim, double aitime) nuclear@55: { nuclear@55: double sec; nuclear@55: if(anim->mTicksPerSecond < 1e-6) { nuclear@55: /* assume time in frames? */ nuclear@55: sec = aitime / 30.0; nuclear@55: } else { nuclear@55: sec = aitime / anim->mTicksPerSecond; nuclear@55: } nuclear@55: return (long)(sec * 1000.0); nuclear@55: }