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 }
|