goat3d

annotate converters/ass2goat/src/main.c @ 56:ca549434dc95

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