goat3d

view converters/ass2goat/src/main.c @ 72:36e39632db75

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