goat3d

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