goat3d

annotate converters/ass2goat/src/main.c @ 57:76d0f55f9d5f

mesh and animation saving looks done...
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 23 Jan 2014 03:57:15 +0200
parents ca549434dc95
children
rev   line source
nuclear@29 1 #include <stdio.h>
nuclear@29 2 #include <stdlib.h>
nuclear@29 3 #include "goat3d.h"
nuclear@29 4 #include "assimp/cimport.h"
nuclear@29 5 #include "assimp/postprocess.h"
nuclear@29 6 #include "assimp/scene.h"
nuclear@29 7
nuclear@55 8 enum {
nuclear@55 9 CONV_SCENE,
nuclear@55 10 CONV_ANIM
nuclear@55 11 };
nuclear@55 12
nuclear@55 13 int convert(const char *infname);
nuclear@55 14 int convert_anim(const char *infname);
nuclear@29 15 void process_material(struct goat3d_material *mtl, struct aiMaterial *aimtl);
nuclear@57 16 void process_mesh(struct goat3d *goat, struct goat3d_mesh *mesh, struct aiMesh *aimesh);
nuclear@29 17 void process_node(struct goat3d *goat, struct goat3d_node *parent, struct aiNode *ainode);
nuclear@55 18 int process_anim(struct goat3d *goat, struct aiAnimation *aianim);
nuclear@55 19 static int output_filename(char *buf, int bufsz, const char *fname, const char *suffix);
nuclear@55 20 static long assimp_time(const struct aiAnimation *anim, double aitime);
nuclear@29 21
nuclear@29 22 int main(int argc, char **argv)
nuclear@29 23 {
nuclear@29 24 int i, num_done = 0;
nuclear@55 25 int conv_targ = CONV_SCENE;
nuclear@29 26
nuclear@29 27 for(i=1; i<argc; i++) {
nuclear@29 28 if(argv[i][0] == '-') {
nuclear@55 29 if(argv[i][2] != 0) {
nuclear@55 30 fprintf(stderr, "invalid option: %s\n", argv[i]);
nuclear@55 31 return 1;
nuclear@55 32 }
nuclear@55 33
nuclear@55 34 switch(argv[i][1]) {
nuclear@55 35 case 'a':
nuclear@55 36 conv_targ = CONV_ANIM;
nuclear@55 37 break;
nuclear@55 38
nuclear@55 39 case 's':
nuclear@55 40 conv_targ = CONV_SCENE;
nuclear@55 41 break;
nuclear@55 42
nuclear@55 43 default:
nuclear@55 44 fprintf(stderr, "invalid option: %s\n", argv[i]);
nuclear@55 45 return 1;
nuclear@55 46 }
nuclear@55 47
nuclear@29 48 } else {
nuclear@55 49 if(conv_targ == CONV_SCENE) {
nuclear@55 50 convert(argv[i]);
nuclear@55 51 } else {
nuclear@55 52 convert_anim(argv[i]);
nuclear@29 53 }
nuclear@29 54 num_done++;
nuclear@29 55 }
nuclear@29 56 }
nuclear@29 57
nuclear@29 58 if(!num_done) {
nuclear@29 59 fprintf(stderr, "you must specify a 3D scene file to convert\n");
nuclear@29 60 return 1;
nuclear@29 61 }
nuclear@29 62
nuclear@29 63 return 0;
nuclear@29 64 }
nuclear@29 65
nuclear@55 66 #define SCE_PPFLAGS \
nuclear@29 67 (aiProcess_Triangulate | \
nuclear@29 68 aiProcess_GenNormals | \
nuclear@29 69 aiProcess_JoinIdenticalVertices | \
nuclear@29 70 aiProcess_CalcTangentSpace | \
nuclear@29 71 aiProcess_LimitBoneWeights | \
nuclear@29 72 aiProcess_GenUVCoords)
nuclear@29 73
nuclear@55 74 #define ANM_PPFLAGS \
nuclear@55 75 (aiProcess_LimitBoneWeights)
nuclear@55 76
nuclear@55 77 int convert(const char *infname)
nuclear@29 78 {
nuclear@55 79 int i, bufsz;
nuclear@29 80 const struct aiScene *aiscn;
nuclear@29 81 struct goat3d *goat;
nuclear@55 82 char *outfname;
nuclear@29 83
nuclear@55 84 bufsz = output_filename(0, 0, infname, "goat3d");
nuclear@55 85 outfname = alloca(bufsz);
nuclear@55 86 output_filename(outfname, bufsz, infname, "goat3d");
nuclear@55 87 printf("converting %s -> %s\n", infname, outfname);
nuclear@55 88
nuclear@55 89
nuclear@55 90 if(!(aiscn = aiImportFile(infname, SCE_PPFLAGS))) {
nuclear@29 91 fprintf(stderr, "failed to import %s\n", infname);
nuclear@29 92 return -1;
nuclear@29 93 }
nuclear@29 94
nuclear@29 95 goat = goat3d_create();
nuclear@29 96
nuclear@29 97 for(i=0; i<(int)aiscn->mNumMaterials; i++) {
nuclear@29 98 struct aiMaterial *aimat = aiscn->mMaterials[i];
nuclear@29 99 struct goat3d_material *mat = goat3d_create_mtl();
nuclear@29 100
nuclear@29 101 process_material(mat, aimat);
nuclear@29 102 goat3d_add_mtl(goat, mat);
nuclear@29 103 }
nuclear@29 104
nuclear@57 105 for(i=0; i<(int)aiscn->mNumMeshes; i++) {
nuclear@57 106 struct aiMesh *aimesh = aiscn->mMeshes[i];
nuclear@57 107 struct goat3d_mesh *mesh = goat3d_create_mesh();
nuclear@57 108
nuclear@57 109 process_mesh(goat, mesh, aimesh);
nuclear@57 110 goat3d_add_mesh(goat, mesh);
nuclear@57 111 }
nuclear@57 112
nuclear@29 113 for(i=0; i<(int)aiscn->mRootNode->mNumChildren; i++) {
nuclear@29 114 process_node(goat, 0, aiscn->mRootNode->mChildren[i]);
nuclear@29 115 }
nuclear@29 116
nuclear@29 117 goat3d_save(goat, outfname);
nuclear@29 118 goat3d_free(goat);
nuclear@29 119 aiReleaseImport(aiscn);
nuclear@29 120 return 0;
nuclear@29 121 }
nuclear@29 122
nuclear@55 123 int convert_anim(const char *infname)
nuclear@55 124 {
nuclear@55 125 int i, bufsz;
nuclear@55 126 const struct aiScene *aiscn;
nuclear@55 127 struct goat3d *goat;
nuclear@55 128 char *outfname;
nuclear@55 129
nuclear@55 130 bufsz = output_filename(0, 0, infname, "goatanim");
nuclear@55 131 outfname = alloca(bufsz);
nuclear@55 132 output_filename(outfname, bufsz, infname, "goatanim");
nuclear@55 133 printf("converting %s -> %s\n", infname, outfname);
nuclear@55 134
nuclear@55 135
nuclear@55 136 if(!(aiscn = aiImportFile(infname, ANM_PPFLAGS))) {
nuclear@55 137 fprintf(stderr, "failed to import %s\n", infname);
nuclear@55 138 return -1;
nuclear@55 139 }
nuclear@55 140
nuclear@55 141 goat = goat3d_create();
nuclear@55 142
nuclear@55 143 for(i=0; i<(int)aiscn->mRootNode->mNumChildren; i++) {
nuclear@55 144 process_node(goat, 0, aiscn->mRootNode->mChildren[i]);
nuclear@55 145 }
nuclear@55 146
nuclear@55 147 for(i=0; i<aiscn->mNumAnimations; i++) {
nuclear@55 148 if(process_anim(goat, aiscn->mAnimations[i]) == -1) {
nuclear@55 149 return -1;
nuclear@55 150 }
nuclear@55 151 }
nuclear@55 152
nuclear@55 153 goat3d_save_anim(goat, outfname);
nuclear@55 154 goat3d_free(goat);
nuclear@55 155 aiReleaseImport(aiscn);
nuclear@55 156 return 0;
nuclear@55 157 }
nuclear@55 158
nuclear@29 159 void process_material(struct goat3d_material *mtl, struct aiMaterial *aimtl)
nuclear@29 160 {
nuclear@29 161 struct aiString aistr;
nuclear@29 162 struct aiColor4D color;
nuclear@29 163 float val;
nuclear@29 164
nuclear@29 165 if(aiGetMaterialString(aimtl, AI_MATKEY_NAME, &aistr) == aiReturn_SUCCESS) {
nuclear@29 166 goat3d_set_mtl_name(mtl, aistr.data);
nuclear@29 167 }
nuclear@29 168
nuclear@29 169 if(aiGetMaterialColor(aimtl, AI_MATKEY_COLOR_DIFFUSE, &color) == aiReturn_SUCCESS) {
nuclear@29 170 goat3d_set_mtl_attrib3f(mtl, GOAT3D_MAT_ATTR_DIFFUSE, color.r, color.g, color.b);
nuclear@29 171 }
nuclear@29 172
nuclear@29 173 if(aiGetMaterialColor(aimtl, AI_MATKEY_COLOR_SPECULAR, &color) == aiReturn_SUCCESS) {
nuclear@29 174 float sstr = 1.0;
nuclear@29 175 aiGetMaterialFloatArray(aimtl, AI_MATKEY_SHININESS_STRENGTH, &sstr, 0);
nuclear@29 176 goat3d_set_mtl_attrib3f(mtl, GOAT3D_MAT_ATTR_SPECULAR, color.r * sstr, color.g * sstr, color.b * sstr);
nuclear@29 177 }
nuclear@29 178
nuclear@29 179 if(aiGetMaterialFloatArray(aimtl, AI_MATKEY_BUMPSCALING, &val, 0) == aiReturn_SUCCESS) {
nuclear@29 180 goat3d_set_mtl_attrib3f(mtl, GOAT3D_MAT_ATTR_BUMP, val, val, val);
nuclear@29 181 }
nuclear@29 182
nuclear@29 183 if(aiGetMaterialFloatArray(aimtl, AI_MATKEY_REFLECTIVITY, &val, 0) == aiReturn_SUCCESS) {
nuclear@29 184 goat3d_set_mtl_attrib1f(mtl, GOAT3D_MAT_ATTR_REFLECTION, val);
nuclear@29 185 }
nuclear@29 186
nuclear@29 187 if(aiGetMaterialFloatArray(aimtl, AI_MATKEY_OPACITY, &val, 0) == aiReturn_SUCCESS) {
nuclear@29 188 goat3d_set_mtl_attrib1f(mtl, GOAT3D_MAT_ATTR_TRANSMISSION, 1.0 - val);
nuclear@29 189 }
nuclear@29 190
nuclear@29 191 if(aiGetMaterialString(aimtl, AI_MATKEY_TEXTURE_DIFFUSE(0), &aistr) == aiReturn_SUCCESS) {
nuclear@29 192 goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_DIFFUSE, aistr.data);
nuclear@29 193 }
nuclear@29 194 if(aiGetMaterialString(aimtl, AI_MATKEY_TEXTURE_SPECULAR(0), &aistr) == aiReturn_SUCCESS) {
nuclear@29 195 goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_SPECULAR, aistr.data);
nuclear@29 196 }
nuclear@29 197 if(aiGetMaterialString(aimtl, AI_MATKEY_TEXTURE_SHININESS(0), &aistr) == aiReturn_SUCCESS) {
nuclear@29 198 goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_SHININESS, aistr.data);
nuclear@29 199 }
nuclear@29 200 if(aiGetMaterialString(aimtl, AI_MATKEY_TEXTURE_NORMALS(0), &aistr) == aiReturn_SUCCESS) {
nuclear@29 201 goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_NORMAL, aistr.data);
nuclear@29 202 }
nuclear@29 203 if(aiGetMaterialString(aimtl, AI_MATKEY_TEXTURE_REFLECTION(0), &aistr) == aiReturn_SUCCESS) {
nuclear@29 204 goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_REFLECTION, aistr.data);
nuclear@29 205 }
nuclear@29 206 if(aiGetMaterialString(aimtl, AI_MATKEY_TEXTURE_OPACITY(0), &aistr) == aiReturn_SUCCESS) {
nuclear@57 207 /* TODO this is semantically inverted... maybe add an alpha attribute? */
nuclear@29 208 goat3d_set_mtl_attrib_map(mtl, GOAT3D_MAT_ATTR_TRANSMISSION, aistr.data);
nuclear@29 209 }
nuclear@29 210 }
nuclear@29 211
nuclear@57 212 void process_mesh(struct goat3d *goat, struct goat3d_mesh *mesh, struct aiMesh *aimesh)
nuclear@57 213 {
nuclear@57 214 int i, num_verts, num_faces;
nuclear@57 215 struct goat3d_material *mtl;
nuclear@57 216
nuclear@57 217 if(aimesh->mName.length > 0) {
nuclear@57 218 goat3d_set_mesh_name(mesh, aimesh->mName.data);
nuclear@57 219 }
nuclear@57 220
nuclear@57 221 if((mtl = goat3d_get_mtl(goat, aimesh->mMaterialIndex))) {
nuclear@57 222 goat3d_set_mesh_mtl(mesh, mtl);
nuclear@57 223 }
nuclear@57 224
nuclear@57 225 num_verts = aimesh->mNumVertices;
nuclear@57 226 num_faces = aimesh->mNumFaces;
nuclear@57 227
nuclear@57 228 for(i=0; i<num_verts; i++) {
nuclear@57 229 struct aiVector3D *v;
nuclear@57 230 struct aiColor4D *col;
nuclear@57 231
nuclear@57 232 v = aimesh->mVertices + i;
nuclear@57 233 goat3d_add_mesh_attrib3f(mesh, GOAT3D_MESH_ATTR_VERTEX, v->x, v->y, v->z);
nuclear@57 234
nuclear@57 235 if(aimesh->mNormals) {
nuclear@57 236 v = aimesh->mNormals + i;
nuclear@57 237 goat3d_add_mesh_attrib3f(mesh, GOAT3D_MESH_ATTR_NORMAL, v->x, v->y, v->z);
nuclear@57 238 }
nuclear@57 239 if(aimesh->mTangents) {
nuclear@57 240 v = aimesh->mTangents + i;
nuclear@57 241 goat3d_add_mesh_attrib3f(mesh, GOAT3D_MESH_ATTR_TANGENT, v->x, v->y, v->z);
nuclear@57 242 }
nuclear@57 243 if(aimesh->mTextureCoords[0]) {
nuclear@57 244 v = aimesh->mTextureCoords[0] + i;
nuclear@57 245 goat3d_add_mesh_attrib2f(mesh, GOAT3D_MESH_ATTR_TEXCOORD, v->x, v->y);
nuclear@57 246 }
nuclear@57 247 if(aimesh->mColors[0]) {
nuclear@57 248 col = aimesh->mColors[0] + i;
nuclear@57 249 goat3d_add_mesh_attrib4f(mesh, GOAT3D_MESH_ATTR_COLOR, col->r, col->g, col->b, col->a);
nuclear@57 250 }
nuclear@57 251 /* TODO: add bones */
nuclear@57 252 }
nuclear@57 253
nuclear@57 254 for(i=0; i<num_faces; i++) {
nuclear@57 255 struct aiFace *face = aimesh->mFaces + i;
nuclear@57 256
nuclear@57 257 goat3d_add_mesh_face(mesh, face->mIndices[0], face->mIndices[1], face->mIndices[2]);
nuclear@57 258 }
nuclear@57 259 }
nuclear@57 260
nuclear@29 261 void process_node(struct goat3d *goat, struct goat3d_node *parent, struct aiNode *ainode)
nuclear@29 262 {
nuclear@29 263 int i;
nuclear@29 264 struct goat3d_node *node;
nuclear@29 265
nuclear@29 266 node = goat3d_create_node();
nuclear@29 267 goat3d_set_node_name(node, ainode->mName.data);
nuclear@29 268
nuclear@29 269 for(i=0; i<ainode->mNumChildren; i++) {
nuclear@29 270 process_node(goat, node, ainode->mChildren[i]);
nuclear@29 271 }
nuclear@29 272
nuclear@29 273 goat3d_add_node(goat, node);
nuclear@29 274 }
nuclear@55 275
nuclear@55 276 int process_anim(struct goat3d *goat, struct aiAnimation *aianim)
nuclear@55 277 {
nuclear@55 278 int i, j, num_nodes, rnodes_count;
nuclear@55 279 const char *anim_name;
nuclear@55 280
nuclear@55 281 if(aianim->mName.length <= 0) {
nuclear@55 282 anim_name = "unnamed";
nuclear@55 283 } else {
nuclear@55 284 anim_name = aianim->mName.data;
nuclear@55 285 }
nuclear@55 286
nuclear@55 287 num_nodes = goat3d_get_node_count(goat);
nuclear@55 288
nuclear@55 289 rnodes_count = 0;
nuclear@55 290 for(i=0; i<num_nodes; i++) {
nuclear@55 291 int anim_idx;
nuclear@55 292 struct goat3d_node *n = goat3d_get_node(goat, i);
nuclear@55 293 /* skip non-root nodes */
nuclear@55 294 if(goat3d_get_node_parent(n)) {
nuclear@55 295 break;
nuclear@55 296 }
nuclear@55 297
nuclear@55 298 /* then add another animation to those root nodes */
nuclear@55 299 anim_idx = goat3d_get_anim_count(n);
nuclear@55 300 goat3d_add_anim(n);
nuclear@55 301 goat3d_use_anim(n, anim_idx);
nuclear@55 302
nuclear@55 303 goat3d_set_anim_name(n, anim_name);
nuclear@55 304 }
nuclear@55 305
nuclear@55 306 /* for each animation "channel" ... */
nuclear@55 307 for(i=0; i<(int)aianim->mNumChannels; i++) {
nuclear@55 308 struct goat3d_node *node;
nuclear@55 309 struct aiNodeAnim *ainodeanim = aianim->mChannels[i];
nuclear@55 310
nuclear@55 311 /* find the node it refers to */
nuclear@55 312 const char *nodename = ainodeanim->mNodeName.data;
nuclear@55 313 if(!(node = goat3d_get_node_by_name(goat, nodename))) {
nuclear@55 314 fprintf(stderr, "failed to process animation for unknown node: %s\n", nodename);
nuclear@55 315 return -1;
nuclear@55 316 }
nuclear@55 317
nuclear@55 318 /* add all the keys ... */
nuclear@55 319 for(j=0; j<(int)ainodeanim->mNumPositionKeys; j++) {
nuclear@55 320 struct aiVectorKey *key = ainodeanim->mPositionKeys + j;
nuclear@55 321 long tm = assimp_time(aianim, key->mTime);
nuclear@55 322 goat3d_set_node_position(node, key->mValue.x, key->mValue.y, key->mValue.z, tm);
nuclear@55 323 }
nuclear@55 324
nuclear@55 325 for(j=0; j<(int)ainodeanim->mNumRotationKeys; j++) {
nuclear@55 326 struct aiQuatKey *key = ainodeanim->mRotationKeys + j;
nuclear@55 327 long tm = assimp_time(aianim, key->mTime);
nuclear@55 328 goat3d_set_node_rotation(node, key->mValue.x, key->mValue.y, key->mValue.z, key->mValue.w, tm);
nuclear@55 329 }
nuclear@55 330
nuclear@55 331 for(j=0; j<(int)ainodeanim->mNumScalingKeys; j++) {
nuclear@55 332 struct aiVectorKey *key = ainodeanim->mScalingKeys + j;
nuclear@55 333 long tm = assimp_time(aianim, key->mTime);
nuclear@55 334 goat3d_set_node_scaling(node, key->mValue.x, key->mValue.y, key->mValue.z, tm);
nuclear@55 335 }
nuclear@55 336 }
nuclear@55 337
nuclear@55 338 return 0;
nuclear@55 339 }
nuclear@55 340
nuclear@55 341 static int output_filename(char *buf, int bufsz, const char *fname, const char *suffix)
nuclear@55 342 {
nuclear@55 343 int reqsz, namesz;
nuclear@55 344 char *tmpfname;
nuclear@55 345 const char *fname_end, *lastdot;
nuclear@55 346
nuclear@55 347 lastdot = strrchr(fname, '.');
nuclear@55 348
nuclear@56 349 fname_end = lastdot ? lastdot : fname + strlen(fname);
nuclear@55 350 namesz = fname_end - fname;
nuclear@55 351 reqsz = namesz + strlen(suffix) + 2; /* plus 1 for the dot */
nuclear@55 352
nuclear@55 353 if(buf && bufsz) {
nuclear@55 354 tmpfname = alloca(namesz + 1);
nuclear@55 355 memcpy(tmpfname, fname, namesz);
nuclear@55 356 tmpfname[namesz] = 0;
nuclear@55 357
nuclear@55 358 if(suffix) {
nuclear@55 359 snprintf(buf, bufsz, "%s.%s", tmpfname, suffix);
nuclear@55 360 } else {
nuclear@55 361 strncpy(buf, tmpfname, bufsz);
nuclear@55 362 }
nuclear@55 363 buf[bufsz - 1] = 0;
nuclear@55 364 }
nuclear@55 365
nuclear@55 366 return reqsz;
nuclear@55 367 }
nuclear@55 368
nuclear@55 369 static long assimp_time(const struct aiAnimation *anim, double aitime)
nuclear@55 370 {
nuclear@55 371 double sec;
nuclear@55 372 if(anim->mTicksPerSecond < 1e-6) {
nuclear@55 373 /* assume time in frames? */
nuclear@55 374 sec = aitime / 30.0;
nuclear@55 375 } else {
nuclear@55 376 sec = aitime / anim->mTicksPerSecond;
nuclear@55 377 }
nuclear@55 378 return (long)(sec * 1000.0);
nuclear@55 379 }