added scr_lvled, a bunch of libraries, and improved framework code
[raydungeon] / libs / goat3d / src / goat3d.c
1 /*
2 goat3d - 3D scene, and animation file format library.
3 Copyright (C) 2013-2023  John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include <string.h>
19 #include <errno.h>
20 #include <ctype.h>
21 #include "goat3d.h"
22 #include "g3dscn.h"
23 #include "log.h"
24 #include "dynarr.h"
25
26 static long read_file(void *buf, size_t bytes, void *uptr);
27 static long write_file(const void *buf, size_t bytes, void *uptr);
28 static long seek_file(long offs, int whence, void *uptr);
29 static char *clean_filename(char *str);
30
31 static const char *def_scn_name = "unnamed";
32
33 #define SETNAME(dest, str)      \
34         do { \
35                 char *tmpname; \
36                 int len = strlen(str); \
37                 if(!(tmpname = malloc(len + 1))) { \
38                         return -1; \
39                 } \
40                 memcpy(tmpname, str, len + 1); \
41                 free(dest); \
42                 dest = tmpname; \
43                 return 0; \
44         } while(0)
45
46
47 GOAT3DAPI struct goat3d *goat3d_create(void)
48 {
49         struct goat3d *g;
50
51         if(!(g = malloc(sizeof *g))) {
52                 return 0;
53         }
54         if(goat3d_init(g) == -1) {
55                 free(g);
56                 return 0;
57         }
58         return g;
59 }
60
61 GOAT3DAPI void goat3d_free(struct goat3d *g)
62 {
63         goat3d_destroy(g);
64         free(g);
65 }
66
67 int goat3d_init(struct goat3d *g)
68 {
69         memset(g, 0, sizeof *g);
70
71         cgm_vcons(&g->ambient, 0.05, 0.05, 0.05);
72
73         if(!(g->materials = dynarr_alloc(0, sizeof *g->materials))) goto err;
74         if(!(g->meshes = dynarr_alloc(0, sizeof *g->meshes))) goto err;
75         if(!(g->lights = dynarr_alloc(0, sizeof *g->lights))) goto err;
76         if(!(g->cameras = dynarr_alloc(0, sizeof *g->cameras))) goto err;
77         if(!(g->nodes = dynarr_alloc(0, sizeof *g->nodes))) goto err;
78         if(!(g->anims = dynarr_alloc(0, sizeof *g->anims))) goto err;
79
80         return 0;
81
82 err:
83         goat3d_destroy(g);
84         return -1;
85 }
86
87 void goat3d_destroy(struct goat3d *g)
88 {
89         goat3d_clear(g);
90
91         dynarr_free(g->materials);
92         dynarr_free(g->meshes);
93         dynarr_free(g->lights);
94         dynarr_free(g->cameras);
95         dynarr_free(g->nodes);
96         dynarr_free(g->anims);
97 }
98
99 void goat3d_clear(struct goat3d *g)
100 {
101         int i, num;
102
103         num = dynarr_size(g->materials);
104         for(i=0; i<num; i++) {
105                 g3dimpl_mtl_destroy(g->materials[i]);
106                 free(g->materials[i]);
107         }
108         DYNARR_CLEAR(g->materials);
109
110         num = dynarr_size(g->meshes);
111         for(i=0; i<num; i++) {
112                 g3dimpl_obj_destroy((struct object*)g->meshes[i]);
113                 free(g->meshes[i]);
114         }
115         DYNARR_CLEAR(g->meshes);
116
117         num = dynarr_size(g->lights);
118         for(i=0; i<num; i++) {
119                 g3dimpl_obj_destroy((struct object*)g->lights[i]);
120                 free(g->lights[i]);
121         }
122         DYNARR_CLEAR(g->lights);
123
124         num = dynarr_size(g->cameras);
125         for(i=0; i<num; i++) {
126                 g3dimpl_obj_destroy((struct object*)g->cameras[i]);
127                 free(g->cameras[i]);
128         }
129         DYNARR_CLEAR(g->cameras);
130
131         num = dynarr_size(g->nodes);
132         for(i=0; i<num; i++) {
133                 goat3d_destroy_node(g->nodes[i]);
134         }
135         DYNARR_CLEAR(g->nodes);
136
137         num = dynarr_size(g->anims);
138         for(i=0; i<num; i++) {
139                 g3dimpl_anim_destroy(g->anims[i]);
140         }
141
142         g->name = 0;
143         g->bbox_valid = 0;
144 }
145
146 GOAT3DAPI void goat3d_setopt(struct goat3d *g, enum goat3d_option opt, int val)
147 {
148         if(val) {
149                 g->flags |= (1 << (int)opt);
150         } else {
151                 g->flags &= ~(1 << (int)opt);
152         }
153 }
154
155 GOAT3DAPI int goat3d_getopt(const struct goat3d *g, enum goat3d_option opt)
156 {
157         return (g->flags >> (int)opt) & 1;
158 }
159
160 GOAT3DAPI int goat3d_load(struct goat3d *g, const char *fname)
161 {
162         int len, res;
163         char *slash;
164         FILE *fp = fopen(fname, "rb");
165         if(!fp) {
166                 goat3d_logmsg(LOG_ERROR, "failed to open file \"%s\" for reading: %s\n", fname, strerror(errno));
167                 return -1;
168         }
169
170         /* if the filename contained any directory components, keep the prefix
171          * to use it as a search path for external mesh file loading
172          */
173         len = strlen(fname);
174         if(!(g->search_path = malloc(len + 1))) {
175                 fclose(fp);
176                 return -1;
177         }
178         memcpy(g->search_path, fname, len + 1);
179
180         if((slash = strrchr(g->search_path, '/'))) {
181                 *slash = 0;
182         } else {
183                 if((slash = strrchr(g->search_path, '\\'))) {
184                         *slash = 0;
185                 } else {
186                         free(g->search_path);
187                         g->search_path = 0;
188                 }
189         }
190
191         if((res = goat3d_load_file(g, fp)) == 0) {
192                 const char *name;
193                 if((name = goat3d_get_name(g)) == def_scn_name) {
194                         goat3d_set_name(g, slash ? slash + 1 : fname);
195                 }
196         }
197         fclose(fp);
198         return res;
199 }
200
201 GOAT3DAPI int goat3d_save(const struct goat3d *g, const char *fname)
202 {
203         int res;
204         FILE *fp = fopen(fname, "wb");
205         if(!fp) {
206                 goat3d_logmsg(LOG_ERROR, "failed to open file \"%s\" for writing: %s\n", fname, strerror(errno));
207                 return -1;
208         }
209
210         res = goat3d_save_file(g, fp);
211         fclose(fp);
212         return res;
213 }
214
215 GOAT3DAPI int goat3d_load_file(struct goat3d *g, FILE *fp)
216 {
217         struct goat3d_io io;
218         io.cls = fp;
219         io.read = read_file;
220         io.write = write_file;
221         io.seek = seek_file;
222
223         return goat3d_load_io(g, &io);
224 }
225
226 GOAT3DAPI int goat3d_save_file(const struct goat3d *g, FILE *fp)
227 {
228         struct goat3d_io io;
229         io.cls = fp;
230         io.read = read_file;
231         io.write = write_file;
232         io.seek = seek_file;
233
234         return goat3d_save_io(g, &io);
235 }
236
237 GOAT3DAPI int goat3d_load_io(struct goat3d *g, struct goat3d_io *io)
238 {
239         return g3dimpl_scnload(g, io);
240 }
241
242 GOAT3DAPI int goat3d_save_io(const struct goat3d *g, struct goat3d_io *io)
243 {
244         if(goat3d_getopt(g, GOAT3D_OPT_SAVEXML)) {
245                 goat3d_logmsg(LOG_ERROR, "saving in the original xml format is no longer supported\n");
246                 return -1;
247         } else if(goat3d_getopt(g, GOAT3D_OPT_SAVETEXT)) {
248                 /* TODO set treestore output format as text */
249         }
250         return g3dimpl_scnsave(g, io);
251 }
252
253 /* save/load animations */
254 GOAT3DAPI int goat3d_load_anim(struct goat3d *g, const char *fname)
255 {
256         int res;
257         FILE *fp;
258
259         if(!(fp = fopen(fname, "rb"))) {
260                 return -1;
261         }
262
263         res = goat3d_load_anim_file(g, fp);
264         fclose(fp);
265         return res;
266 }
267
268 GOAT3DAPI int goat3d_save_anim(const struct goat3d *g, const char *fname)
269 {
270         int res;
271         FILE *fp;
272
273         if(!(fp = fopen(fname, "wb"))) {
274                 return -1;
275         }
276
277         res = goat3d_save_anim_file(g, fp);
278         fclose(fp);
279         return res;
280 }
281
282 GOAT3DAPI int goat3d_load_anim_file(struct goat3d *g, FILE *fp)
283 {
284         struct goat3d_io io;
285         io.cls = fp;
286         io.read = read_file;
287         io.write = write_file;
288         io.seek = seek_file;
289
290         return goat3d_load_anim_io(g, &io);
291 }
292
293 GOAT3DAPI int goat3d_save_anim_file(const struct goat3d *g, FILE *fp)
294 {
295         struct goat3d_io io;
296         io.cls = fp;
297         io.read = read_file;
298         io.write = write_file;
299         io.seek = seek_file;
300
301         return goat3d_save_anim_io(g, &io);
302 }
303
304 GOAT3DAPI int goat3d_load_anim_io(struct goat3d *g, struct goat3d_io *io)
305 {
306         return g3dimpl_anmload(g, io);
307 }
308
309 GOAT3DAPI int goat3d_save_anim_io(const struct goat3d *g, struct goat3d_io *io)
310 {
311         if(goat3d_getopt(g, GOAT3D_OPT_SAVEXML)) {
312                 goat3d_logmsg(LOG_ERROR, "saving in the original xml format is no longer supported\n");
313                 return -1;
314         } else if(goat3d_getopt(g, GOAT3D_OPT_SAVETEXT)) {
315                 /* TODO set treestore save format as text */
316         }
317         return g3dimpl_anmsave(g, io);
318 }
319
320
321 GOAT3DAPI int goat3d_set_name(struct goat3d *g, const char *name)
322 {
323         int len = strlen(name);
324
325         free(g->name);
326         if(!(g->name = malloc(len + 1))) {
327                 return -1;
328         }
329         memcpy(g->name, name, len + 1);
330         return 0;
331 }
332
333 GOAT3DAPI const char *goat3d_get_name(const struct goat3d *g)
334 {
335         return g->name ? g->name : def_scn_name;
336 }
337
338 GOAT3DAPI void goat3d_set_ambient(struct goat3d *g, const float *amb)
339 {
340         cgm_vcons(&g->ambient, amb[0], amb[1], amb[2]);
341 }
342
343 GOAT3DAPI void goat3d_set_ambient3f(struct goat3d *g, float ar, float ag, float ab)
344 {
345         cgm_vcons(&g->ambient, ar, ag, ab);
346 }
347
348 GOAT3DAPI const float *goat3d_get_ambient(const struct goat3d *g)
349 {
350         return &g->ambient.x;
351 }
352
353 GOAT3DAPI int goat3d_get_bounds(const struct goat3d *g, float *bmin, float *bmax)
354 {
355         int i, num_nodes, num_meshes;
356         struct aabox bbox;
357
358         if(!g->bbox_valid) {
359                 g3dimpl_aabox_init((struct aabox*)&g->bbox);
360
361                 if(dynarr_empty(g->nodes)) {
362 use_mesh_bounds:
363                         num_meshes = dynarr_size(g->meshes);
364                         for(i=0; i<num_meshes; i++) {
365                                 g3dimpl_mesh_bounds(&bbox, g->meshes[i], 0);
366                                 g3dimpl_aabox_union((struct aabox*)&g->bbox, &g->bbox, &bbox);
367                         }
368                 } else {
369                         num_nodes = dynarr_size(g->nodes);
370                         for(i=0; i<num_nodes; i++) {
371                                 if(g->nodes[i]->parent) {
372                                         continue;
373                                 }
374                                 g3dimpl_node_bounds(&bbox, g->nodes[i]);
375                                 g3dimpl_aabox_union((struct aabox*)&g->bbox, &g->bbox, &bbox);
376                         }
377
378                         /* in case the nodes are junk */
379                         if(g->bbox.bmin.x > g->bbox.bmax.x) {
380                                 goto use_mesh_bounds;
381                         }
382                 }
383                 ((struct goat3d*)g)->bbox_valid = 1;
384         }
385
386         if(g->bbox.bmin.x > g->bbox.bmax.x) {
387                 return -1;
388         }
389
390         bmin[0] = g->bbox.bmin.x;
391         bmin[1] = g->bbox.bmin.y;
392         bmin[2] = g->bbox.bmin.z;
393         bmax[0] = g->bbox.bmax.x;
394         bmax[1] = g->bbox.bmax.y;
395         bmax[2] = g->bbox.bmax.z;
396         return 0;
397 }
398
399 // ---- materials ----
400 GOAT3DAPI int goat3d_add_mtl(struct goat3d *g, struct goat3d_material *mtl)
401 {
402         struct goat3d_material **newarr;
403         mtl->idx = dynarr_size(g->materials);
404         if(!(newarr = dynarr_push(g->materials, &mtl))) {
405                 return -1;
406         }
407         g->materials = newarr;
408         return 0;
409 }
410
411 GOAT3DAPI int goat3d_get_mtl_count(struct goat3d *g)
412 {
413         return dynarr_size(g->materials);
414 }
415
416 GOAT3DAPI struct goat3d_material *goat3d_get_mtl(struct goat3d *g, int idx)
417 {
418         return g->materials[idx];
419 }
420
421 GOAT3DAPI struct goat3d_material *goat3d_get_mtl_by_name(struct goat3d *g, const char *name)
422 {
423         int i, num = dynarr_size(g->materials);
424         for(i=0; i<num; i++) {
425                 if(strcmp(g->materials[i]->name, name) == 0) {
426                         return g->materials[i];
427                 }
428         }
429         return 0;
430 }
431
432 GOAT3DAPI struct goat3d_material *goat3d_create_mtl(void)
433 {
434         struct goat3d_material *mtl;
435         if(!(mtl = malloc(sizeof *mtl))) {
436                 return 0;
437         }
438         g3dimpl_mtl_init(mtl);
439         return mtl;
440 }
441
442 GOAT3DAPI void goat3d_destroy_mtl(struct goat3d_material *mtl)
443 {
444         g3dimpl_mtl_destroy(mtl);
445         free(mtl);
446 }
447
448 GOAT3DAPI int goat3d_set_mtl_name(struct goat3d_material *mtl, const char *name)
449 {
450         SETNAME(mtl->name, name);
451 }
452
453 GOAT3DAPI const char *goat3d_get_mtl_name(const struct goat3d_material *mtl)
454 {
455         return mtl->name;
456 }
457
458 GOAT3DAPI int goat3d_set_mtl_attrib(struct goat3d_material *mtl, const char *attrib, const float *val)
459 {
460         struct material_attrib *ma = g3dimpl_mtl_getattr(mtl, attrib);
461         if(!ma) return -1;
462         cgm_wcons(&ma->value, val[0], val[1], val[2], val[3]);
463         return 0;
464 }
465
466 GOAT3DAPI int goat3d_set_mtl_attrib1f(struct goat3d_material *mtl, const char *attrib, float val)
467 {
468         return goat3d_set_mtl_attrib4f(mtl, attrib, val, 0, 0, 1);
469 }
470
471 GOAT3DAPI int goat3d_set_mtl_attrib3f(struct goat3d_material *mtl, const char *attrib, float r, float g, float b)
472 {
473         return goat3d_set_mtl_attrib4f(mtl, attrib, r, g, b, 1);
474 }
475
476 GOAT3DAPI int goat3d_set_mtl_attrib4f(struct goat3d_material *mtl, const char *attrib, float r, float g, float b, float a)
477 {
478         struct material_attrib *ma = g3dimpl_mtl_getattr(mtl, attrib);
479         if(!ma) return -1;
480         cgm_wcons(&ma->value, r, g, b, a);
481         return 0;
482 }
483
484 GOAT3DAPI const float *goat3d_get_mtl_attrib(struct goat3d_material *mtl, const char *attrib)
485 {
486         struct material_attrib *ma = g3dimpl_mtl_findattr(mtl, attrib);
487         return ma ? &ma->value.x : 0;
488 }
489
490 GOAT3DAPI int goat3d_set_mtl_attrib_map(struct goat3d_material *mtl, const char *attrib, const char *mapname)
491 {
492         int len;
493         char *tmp;
494         struct material_attrib *ma;
495
496         len = strlen(mapname);
497         if(!(tmp = malloc(len + 1))) {
498                 return -1;
499         }
500         memcpy(tmp, mapname, len + 1);
501
502         if(!(ma = g3dimpl_mtl_getattr(mtl, attrib))) {
503                 free(tmp);
504                 return -1;
505         }
506         free(ma->map);
507         ma->map = tmp;
508         tmp = clean_filename(ma->map);
509         if(tmp != ma->map) {
510                 memmove(ma->map, tmp, len - (tmp - ma->map) + 1);
511         }
512         return 0;
513 }
514
515 GOAT3DAPI const char *goat3d_get_mtl_attrib_map(struct goat3d_material *mtl, const char *attrib)
516 {
517         struct material_attrib *ma = g3dimpl_mtl_findattr(mtl, attrib);
518         return ma ? ma->map : 0;
519 }
520
521 GOAT3DAPI const char *goat3d_get_mtl_attrib_name(struct goat3d_material *mtl, int idx)
522 {
523         return mtl->attrib[idx].name;
524 }
525
526 GOAT3DAPI int goat3d_get_mtl_attrib_count(struct goat3d_material *mtl)
527 {
528         return dynarr_size(mtl->attrib);
529 }
530
531 // ---- meshes ----
532 GOAT3DAPI int goat3d_add_mesh(struct goat3d *g, struct goat3d_mesh *mesh)
533 {
534         struct goat3d_mesh **arr;
535         if(!(arr = dynarr_push(g->meshes, &mesh))) {
536                 return -1;
537         }
538         g->meshes = arr;
539         return 0;
540 }
541
542 GOAT3DAPI int goat3d_get_mesh_count(struct goat3d *g)
543 {
544         return dynarr_size(g->meshes);
545 }
546
547 GOAT3DAPI struct goat3d_mesh *goat3d_get_mesh(struct goat3d *g, int idx)
548 {
549         return g->meshes[idx];
550 }
551
552 GOAT3DAPI struct goat3d_mesh *goat3d_get_mesh_by_name(struct goat3d *g, const char *name)
553 {
554         int i, num = dynarr_size(g->meshes);
555         for(i=0; i<num; i++) {
556                 if(strcmp(g->meshes[i]->name, name) == 0) {
557                         return g->meshes[i];
558                 }
559         }
560         return 0;
561 }
562
563 GOAT3DAPI struct goat3d_mesh *goat3d_create_mesh(void)
564 {
565         struct goat3d_mesh *m;
566
567         if(!(m = malloc(sizeof *m))) {
568                 return 0;
569         }
570         if(g3dimpl_obj_init((struct object*)m, OBJTYPE_MESH) == -1) {
571                 free(m);
572                 return 0;
573         }
574         return m;
575 }
576
577 GOAT3DAPI void goat3d_destroy_mesh(struct goat3d_mesh *mesh)
578 {
579         g3dimpl_obj_destroy((struct object*)mesh);
580         free(mesh);
581 }
582
583 GOAT3DAPI int goat3d_set_mesh_name(struct goat3d_mesh *mesh, const char *name)
584 {
585         SETNAME(mesh->name, name);
586 }
587
588 GOAT3DAPI const char *goat3d_get_mesh_name(const struct goat3d_mesh *mesh)
589 {
590         return mesh->name;
591 }
592
593 GOAT3DAPI void goat3d_set_mesh_mtl(struct goat3d_mesh *mesh, struct goat3d_material *mtl)
594 {
595         mesh->mtl = mtl;
596 }
597
598 GOAT3DAPI struct goat3d_material *goat3d_get_mesh_mtl(struct goat3d_mesh *mesh)
599 {
600         return mesh->mtl;
601 }
602
603 GOAT3DAPI int goat3d_get_mesh_vertex_count(struct goat3d_mesh *mesh)
604 {
605         return dynarr_size(mesh->vertices);
606 }
607
608 GOAT3DAPI int goat3d_get_mesh_attrib_count(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib)
609 {
610         switch(attrib) {
611         case GOAT3D_MESH_ATTR_VERTEX:
612                 return dynarr_size(mesh->vertices);
613         case GOAT3D_MESH_ATTR_NORMAL:
614                 return dynarr_size(mesh->normals);
615         case GOAT3D_MESH_ATTR_TANGENT:
616                 return dynarr_size(mesh->tangents);
617         case GOAT3D_MESH_ATTR_TEXCOORD:
618                 return dynarr_size(mesh->texcoords);
619         case GOAT3D_MESH_ATTR_SKIN_WEIGHT:
620                 return dynarr_size(mesh->skin_weights);
621         case GOAT3D_MESH_ATTR_SKIN_MATRIX:
622                 return dynarr_size(mesh->skin_matrices);
623         case GOAT3D_MESH_ATTR_COLOR:
624                 return dynarr_size(mesh->colors);
625         default:
626                 break;
627         }
628         return 0;
629 }
630
631 GOAT3DAPI int goat3d_get_mesh_face_count(struct goat3d_mesh *mesh)
632 {
633         return dynarr_size(mesh->faces);
634 }
635
636 #define SET_VERTEX_DATA(arr, p, n) \
637         do { \
638                 void *tmp = dynarr_resize(arr, n); \
639                 if(!tmp) { \
640                         goat3d_logmsg(LOG_ERROR, "failed to resize vertex array (%d)\n", n); \
641                         return -1; \
642                 } \
643                 arr = tmp; \
644                 memcpy(arr, p, n * sizeof *arr); \
645         } while(0)
646
647 GOAT3DAPI int goat3d_set_mesh_attribs(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib, const void *data, int vnum)
648 {
649         if(attrib == GOAT3D_MESH_ATTR_VERTEX) {
650                 SET_VERTEX_DATA(mesh->vertices, data, vnum);
651                 return 0;
652         }
653
654         if(vnum != dynarr_size(mesh->vertices)) {
655                 goat3d_logmsg(LOG_ERROR, "trying to set mesh attrib data with number of elements different than the vertex array\n");
656                 return -1;
657         }
658
659         switch(attrib) {
660         case GOAT3D_MESH_ATTR_NORMAL:
661                 SET_VERTEX_DATA(mesh->normals, data, vnum);
662                 break;
663         case GOAT3D_MESH_ATTR_TANGENT:
664                 SET_VERTEX_DATA(mesh->tangents, data, vnum);
665                 break;
666         case GOAT3D_MESH_ATTR_TEXCOORD:
667                 SET_VERTEX_DATA(mesh->texcoords, data, vnum);
668                 break;
669         case GOAT3D_MESH_ATTR_SKIN_WEIGHT:
670                 SET_VERTEX_DATA(mesh->skin_weights, data, vnum);
671                 break;
672         case GOAT3D_MESH_ATTR_SKIN_MATRIX:
673                 SET_VERTEX_DATA(mesh->skin_matrices, data, vnum);
674                 break;
675         case GOAT3D_MESH_ATTR_COLOR:
676                 SET_VERTEX_DATA(mesh->colors, data, vnum);
677         default:
678                 goat3d_logmsg(LOG_ERROR, "trying to set unknown vertex attrib: %d\n", attrib);
679                 return -1;
680         }
681         return 0;
682 }
683
684 GOAT3DAPI int goat3d_add_mesh_attrib1f(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib,
685                 float val)
686 {
687         return goat3d_add_mesh_attrib4f(mesh, attrib, val, 0, 0, 1);
688 }
689
690 GOAT3DAPI int goat3d_add_mesh_attrib2f(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib,
691                 float x, float y)
692 {
693         return goat3d_add_mesh_attrib4f(mesh, attrib, x, y, 0, 1);
694 }
695
696 GOAT3DAPI int goat3d_add_mesh_attrib3f(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib,
697                 float x, float y, float z)
698 {
699         return goat3d_add_mesh_attrib4f(mesh, attrib, x, y, z, 1);
700 }
701
702 GOAT3DAPI int goat3d_add_mesh_attrib4f(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib,
703                 float x, float y, float z, float w)
704 {
705         float vec[4];
706         int4 intvec;
707         void *tmp;
708
709         switch(attrib) {
710         case GOAT3D_MESH_ATTR_VERTEX:
711                 cgm_vcons((cgm_vec3*)vec, x, y, z);
712                 if(!(tmp = dynarr_push(mesh->vertices, vec))) {
713                         goto err;
714                 }
715                 mesh->vertices = tmp;
716                 break;
717
718         case GOAT3D_MESH_ATTR_NORMAL:
719                 cgm_vcons((cgm_vec3*)vec, x, y, z);
720                 if(!(tmp = dynarr_push(mesh->normals, vec))) {
721                         goto err;
722                 }
723                 mesh->normals = tmp;
724                 break;
725
726         case GOAT3D_MESH_ATTR_TANGENT:
727                 cgm_vcons((cgm_vec3*)vec, x, y, z);
728                 if(!(tmp = dynarr_push(mesh->tangents, vec))) {
729                         goto err;
730                 }
731                 mesh->tangents = tmp;
732                 break;
733
734         case GOAT3D_MESH_ATTR_TEXCOORD:
735                 cgm_vcons((cgm_vec3*)vec, x, y, 0);
736                 if(!(tmp = dynarr_push(mesh->texcoords, vec))) {
737                         goto err;
738                 }
739                 mesh->texcoords = tmp;
740                 break;
741
742         case GOAT3D_MESH_ATTR_SKIN_WEIGHT:
743                 cgm_wcons((cgm_vec4*)vec, x, y, z, w);
744                 if(!(tmp = dynarr_push(mesh->skin_weights, vec))) {
745                         goto err;
746                 }
747                 mesh->skin_weights = tmp;
748                 break;
749
750         case GOAT3D_MESH_ATTR_SKIN_MATRIX:
751                 intvec.x = x;
752                 intvec.y = y;
753                 intvec.z = z;
754                 intvec.w = w;
755                 if(!(tmp = dynarr_push(mesh->skin_matrices, &intvec))) {
756                         goto err;
757                 }
758                 mesh->skin_matrices = tmp;
759                 break;
760
761         case GOAT3D_MESH_ATTR_COLOR:
762                 cgm_wcons((cgm_vec4*)vec, x, y, z, w);
763                 if(!(tmp = dynarr_push(mesh->colors, vec))) {
764                         goto err;
765                 }
766                 mesh->colors = tmp;
767
768         default:
769                 goat3d_logmsg(LOG_ERROR, "trying to add unknown vertex attrib: %d\n", attrib);
770                 return -1;
771         }
772         return 0;
773
774 err:
775         goat3d_logmsg(LOG_ERROR, "failed to push vertex attrib\n");
776         return -1;
777 }
778
779 GOAT3DAPI void *goat3d_get_mesh_attribs(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib)
780 {
781         return goat3d_get_mesh_attrib(mesh, attrib, 0);
782 }
783
784 GOAT3DAPI void *goat3d_get_mesh_attrib(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib, int idx)
785 {
786         switch(attrib) {
787         case GOAT3D_MESH_ATTR_VERTEX:
788                 return dynarr_empty(mesh->vertices) ? 0 : mesh->vertices + idx;
789         case GOAT3D_MESH_ATTR_NORMAL:
790                 return dynarr_empty(mesh->normals) ? 0 : mesh->normals + idx;
791         case GOAT3D_MESH_ATTR_TANGENT:
792                 return dynarr_empty(mesh->tangents) ? 0 : mesh->tangents + idx;
793         case GOAT3D_MESH_ATTR_TEXCOORD:
794                 return dynarr_empty(mesh->texcoords) ? 0 : mesh->texcoords + idx;
795         case GOAT3D_MESH_ATTR_SKIN_WEIGHT:
796                 return dynarr_empty(mesh->skin_weights) ? 0 : mesh->skin_weights + idx;
797         case GOAT3D_MESH_ATTR_SKIN_MATRIX:
798                 return dynarr_empty(mesh->skin_matrices) ? 0 : mesh->skin_matrices + idx;
799         case GOAT3D_MESH_ATTR_COLOR:
800                 return dynarr_empty(mesh->colors) ? 0 : mesh->colors + idx;
801         default:
802                 break;
803         }
804         return 0;
805 }
806
807
808 GOAT3DAPI int goat3d_set_mesh_faces(struct goat3d_mesh *mesh, const int *data, int num)
809 {
810         void *tmp;
811         if(!(tmp = dynarr_resize(mesh->faces, num))) {
812                 goat3d_logmsg(LOG_ERROR, "failed to resize face array (%d)\n", num);
813                 return -1;
814         }
815         mesh->faces = tmp;
816         memcpy(mesh->faces, data, num * sizeof *mesh->faces);
817         return 0;
818 }
819
820 GOAT3DAPI int goat3d_add_mesh_face(struct goat3d_mesh *mesh, int a, int b, int c)
821 {
822         void *tmp;
823         struct face face;
824
825         face.v[0] = a;
826         face.v[1] = b;
827         face.v[2] = c;
828
829         if(!(tmp = dynarr_push(mesh->faces, &face))) {
830                 goat3d_logmsg(LOG_ERROR, "failed to add face\n");
831                 return -1;
832         }
833         mesh->faces = tmp;
834         return 0;
835 }
836
837 GOAT3DAPI int *goat3d_get_mesh_faces(struct goat3d_mesh *mesh)
838 {
839         return goat3d_get_mesh_face(mesh, 0);
840 }
841
842 GOAT3DAPI int *goat3d_get_mesh_face(struct goat3d_mesh *mesh, int idx)
843 {
844         return dynarr_empty(mesh->faces) ? 0 : mesh->faces[idx].v;
845 }
846
847 // immedate mode state
848 static enum goat3d_im_primitive im_prim;
849 static struct goat3d_mesh *im_mesh;
850 static cgm_vec3 im_norm, im_tang;
851 static cgm_vec2 im_texcoord;
852 static cgm_vec4 im_skinw, im_color = {1, 1, 1, 1};
853 static int4 im_skinmat;
854 static int im_use[NUM_GOAT3D_MESH_ATTRIBS];
855
856
857 GOAT3DAPI void goat3d_begin(struct goat3d_mesh *mesh, enum goat3d_im_primitive prim)
858 {
859         DYNARR_CLEAR(mesh->vertices);
860         DYNARR_CLEAR(mesh->normals);
861         DYNARR_CLEAR(mesh->tangents);
862         DYNARR_CLEAR(mesh->texcoords);
863         DYNARR_CLEAR(mesh->skin_weights);
864         DYNARR_CLEAR(mesh->skin_matrices);
865         DYNARR_CLEAR(mesh->colors);
866         DYNARR_CLEAR(mesh->faces);
867
868         im_mesh = mesh;
869         memset(im_use, 0, sizeof im_use);
870
871         im_prim = prim;
872 }
873
874 GOAT3DAPI void goat3d_end(void)
875 {
876         int i, vidx, num_faces, num_quads;
877         void *tmp;
878
879         switch(im_prim) {
880         case GOAT3D_TRIANGLES:
881                 {
882                         num_faces = dynarr_size(im_mesh->vertices) / 3;
883                         if(!(tmp = dynarr_resize(im_mesh->faces, num_faces))) {
884                                 return;
885                         }
886                         im_mesh->faces = tmp;
887
888                         vidx = 0;
889                         for(i=0; i<num_faces; i++) {
890                                 im_mesh->faces[i].v[0] = vidx++;
891                                 im_mesh->faces[i].v[1] = vidx++;
892                                 im_mesh->faces[i].v[2] = vidx++;
893                         }
894                 }
895                 break;
896
897         case GOAT3D_QUADS:
898                 {
899                         num_quads = dynarr_size(im_mesh->vertices) / 4;
900                         if(!(tmp = dynarr_resize(im_mesh->faces, num_quads * 2))) {
901                                 return;
902                         }
903                         im_mesh->faces = tmp;
904
905                         vidx = 0;
906                         for(i=0; i<num_quads; i++) {
907                                 im_mesh->faces[i * 2].v[0] = vidx;
908                                 im_mesh->faces[i * 2].v[1] = vidx + 1;
909                                 im_mesh->faces[i * 2].v[2] = vidx + 2;
910
911                                 im_mesh->faces[i * 2 + 1].v[0] = vidx;
912                                 im_mesh->faces[i * 2 + 1].v[1] = vidx + 2;
913                                 im_mesh->faces[i * 2 + 1].v[2] = vidx + 3;
914
915                                 vidx += 4;
916                         }
917                 }
918                 break;
919
920         default:
921                 break;
922         }
923 }
924
925 GOAT3DAPI void goat3d_vertex3f(float x, float y, float z)
926 {
927         void *tmp;
928         cgm_vec3 v;
929
930         cgm_vcons(&v, x, y, z);
931         if(!(tmp = dynarr_push(im_mesh->vertices, &v))) {
932                 return;
933         }
934         im_mesh->vertices = tmp;
935
936         if(im_use[GOAT3D_MESH_ATTR_NORMAL]) {
937                 if((tmp = dynarr_push(im_mesh->normals, &im_norm))) {
938                         im_mesh->normals = tmp;
939                 }
940         }
941         if(im_use[GOAT3D_MESH_ATTR_TANGENT]) {
942                 if((tmp = dynarr_push(im_mesh->tangents, &im_tang))) {
943                         im_mesh->tangents = tmp;
944                 }
945         }
946         if(im_use[GOAT3D_MESH_ATTR_TEXCOORD]) {
947                 if((tmp = dynarr_push(im_mesh->texcoords, &im_texcoord))) {
948                         im_mesh->texcoords = tmp;
949                 }
950         }
951         if(im_use[GOAT3D_MESH_ATTR_SKIN_WEIGHT]) {
952                 if((tmp = dynarr_push(im_mesh->skin_weights, &im_skinw))) {
953                         im_mesh->skin_weights = tmp;
954                 }
955         }
956         if(im_use[GOAT3D_MESH_ATTR_SKIN_MATRIX]) {
957                 if((tmp = dynarr_push(im_mesh->skin_matrices, &im_skinmat))) {
958                         im_mesh->skin_matrices = tmp;
959                 }
960         }
961         if(im_use[GOAT3D_MESH_ATTR_COLOR]) {
962                 if((tmp = dynarr_push(im_mesh->colors, &im_color))) {
963                         im_mesh->colors = tmp;
964                 }
965         }
966 }
967
968 GOAT3DAPI void goat3d_normal3f(float x, float y, float z)
969 {
970         cgm_vcons(&im_norm, x, y, z);
971         im_use[GOAT3D_MESH_ATTR_NORMAL] = 1;
972 }
973
974 GOAT3DAPI void goat3d_tangent3f(float x, float y, float z)
975 {
976         cgm_vcons(&im_tang, x, y, z);
977         im_use[GOAT3D_MESH_ATTR_TANGENT] = 1;
978 }
979
980 GOAT3DAPI void goat3d_texcoord2f(float x, float y)
981 {
982         im_texcoord.x = x;
983         im_texcoord.y = y;
984         im_use[GOAT3D_MESH_ATTR_TEXCOORD] = 1;
985 }
986
987 GOAT3DAPI void goat3d_skin_weight4f(float x, float y, float z, float w)
988 {
989         cgm_wcons(&im_skinw, x, y, z, w);
990         im_use[GOAT3D_MESH_ATTR_SKIN_WEIGHT] = 1;
991 }
992
993 GOAT3DAPI void goat3d_skin_matrix4i(int x, int y, int z, int w)
994 {
995         im_skinmat.x = x;
996         im_skinmat.y = y;
997         im_skinmat.z = z;
998         im_skinmat.w = w;
999         im_use[GOAT3D_MESH_ATTR_SKIN_MATRIX] = 1;
1000 }
1001
1002 GOAT3DAPI void goat3d_color3f(float x, float y, float z)
1003 {
1004         goat3d_color4f(x, y, z, 1.0f);
1005 }
1006
1007 GOAT3DAPI void goat3d_color4f(float x, float y, float z, float w)
1008 {
1009         cgm_wcons(&im_color, x, y, z, w);
1010         im_use[GOAT3D_MESH_ATTR_COLOR] = 1;
1011 }
1012
1013 GOAT3DAPI void goat3d_get_mesh_bounds(const struct goat3d_mesh *mesh, float *bmin, float *bmax)
1014 {
1015         struct aabox box;
1016
1017         g3dimpl_mesh_bounds(&box, (struct goat3d_mesh*)mesh, 0);
1018
1019         bmin[0] = box.bmin.x;
1020         bmin[1] = box.bmin.y;
1021         bmin[2] = box.bmin.z;
1022         bmax[0] = box.bmax.x;
1023         bmax[1] = box.bmax.y;
1024         bmax[2] = box.bmax.z;
1025 }
1026
1027 /* lights */
1028 GOAT3DAPI int goat3d_add_light(struct goat3d *g, struct goat3d_light *lt)
1029 {
1030         struct goat3d_light **arr;
1031         if(!(arr = dynarr_push(g->lights, &lt))) {
1032                 return -1;
1033         }
1034         g->lights = arr;
1035         return 0;
1036 }
1037
1038 GOAT3DAPI int goat3d_get_light_count(struct goat3d *g)
1039 {
1040         return dynarr_size(g->lights);
1041 }
1042
1043 GOAT3DAPI struct goat3d_light *goat3d_get_light(struct goat3d *g, int idx)
1044 {
1045         return g->lights[idx];
1046 }
1047
1048 GOAT3DAPI struct goat3d_light *goat3d_get_light_by_name(struct goat3d *g, const char *name)
1049 {
1050         int i, num = dynarr_size(g->lights);
1051         for(i=0; i<num; i++) {
1052                 if(strcmp(g->lights[i]->name, name) == 0) {
1053                         return g->lights[i];
1054                 }
1055         }
1056         return 0;
1057 }
1058
1059
1060 GOAT3DAPI struct goat3d_light *goat3d_create_light(void)
1061 {
1062         struct goat3d_light *lt;
1063
1064         if(!(lt = malloc(sizeof *lt))) {
1065                 return 0;
1066         }
1067         if(g3dimpl_obj_init((struct object*)lt, OBJTYPE_LIGHT) == -1) {
1068                 free(lt);
1069                 return 0;
1070         }
1071         return lt;
1072 }
1073
1074 GOAT3DAPI void goat3d_destroy_light(struct goat3d_light *lt)
1075 {
1076         g3dimpl_obj_destroy((struct object*)lt);
1077         free(lt);
1078 }
1079
1080 GOAT3DAPI int goat3d_set_light_name(struct goat3d_light *lt, const char *name)
1081 {
1082         SETNAME(lt->name, name);
1083 }
1084
1085 GOAT3DAPI const char *goat3d_get_light_name(const struct goat3d_light *lt)
1086 {
1087         return lt->name;
1088 }
1089
1090 /* cameras */
1091 GOAT3DAPI int goat3d_add_camera(struct goat3d *g, struct goat3d_camera *cam)
1092 {
1093         struct goat3d_camera **arr;
1094         if(!(arr = dynarr_push(g->cameras, &cam))) {
1095                 return -1;
1096         }
1097         g->cameras = arr;
1098         return 0;
1099 }
1100
1101 GOAT3DAPI int goat3d_get_camera_count(struct goat3d *g)
1102 {
1103         return dynarr_size(g->cameras);
1104 }
1105
1106 GOAT3DAPI struct goat3d_camera *goat3d_get_camera(struct goat3d *g, int idx)
1107 {
1108         return g->cameras[idx];
1109 }
1110
1111 GOAT3DAPI struct goat3d_camera *goat3d_get_camera_by_name(struct goat3d *g, const char *name)
1112 {
1113         int i, num = dynarr_size(g->cameras);
1114         for(i=0; i<num; i++) {
1115                 if(strcmp(g->cameras[i]->name, name) == 0) {
1116                         return g->cameras[i];
1117                 }
1118         }
1119         return 0;
1120 }
1121
1122 GOAT3DAPI struct goat3d_camera *goat3d_create_camera(void)
1123 {
1124         struct goat3d_camera *cam;
1125
1126         if(!(cam = malloc(sizeof *cam))) {
1127                 return 0;
1128         }
1129         if(g3dimpl_obj_init((struct object*)cam, OBJTYPE_CAMERA) == -1) {
1130                 free(cam);
1131                 return 0;
1132         }
1133         return cam;
1134 }
1135
1136 GOAT3DAPI void goat3d_destroy_camera(struct goat3d_camera *cam)
1137 {
1138         g3dimpl_obj_destroy((struct object*)cam);
1139         free(cam);
1140 }
1141
1142 GOAT3DAPI int goat3d_set_camera_name(struct goat3d_camera *cam, const char *name)
1143 {
1144         SETNAME(cam->name, name);
1145 }
1146
1147 GOAT3DAPI const char *goat3d_get_camera_name(const struct goat3d_camera *cam)
1148 {
1149         return cam->name;
1150 }
1151
1152 /* node */
1153 GOAT3DAPI int goat3d_add_node(struct goat3d *g, struct goat3d_node *node)
1154 {
1155         struct goat3d_node **arr;
1156         if(!(arr = dynarr_push(g->nodes, &node))) {
1157                 return -1;
1158         }
1159         g->nodes = arr;
1160         return 0;
1161 }
1162
1163 GOAT3DAPI int goat3d_get_node_count(struct goat3d *g)
1164 {
1165         return dynarr_size(g->nodes);
1166 }
1167
1168 GOAT3DAPI struct goat3d_node *goat3d_get_node(struct goat3d *g, int idx)
1169 {
1170         return g->nodes[idx];
1171 }
1172
1173 GOAT3DAPI struct goat3d_node *goat3d_get_node_by_name(struct goat3d *g, const char *name)
1174 {
1175         int i, num = dynarr_size(g->nodes);
1176         for(i=0; i<num; i++) {
1177                 if(strcmp(g->nodes[i]->name, name) == 0) {
1178                         return g->nodes[i];
1179                 }
1180         }
1181         return 0;
1182 }
1183
1184 GOAT3DAPI struct goat3d_node *goat3d_create_node(void)
1185 {
1186         struct goat3d_node *node;
1187
1188         if(!(node = calloc(1, sizeof *node))) {
1189                 return 0;
1190         }
1191         node->type = GOAT3D_NODE_NULL;
1192         node->obj = 0;
1193         node->child_count = 0;
1194
1195         node->rot.w = node->arot.w = 1;
1196         cgm_vcons(&node->scale, 1, 1, 1);
1197         cgm_midentity(node->matrix);
1198
1199         return node;
1200 }
1201
1202 GOAT3DAPI void goat3d_destroy_node(struct goat3d_node *node)
1203 {
1204         if(!node) return;
1205         free(node->name);
1206         free(node);
1207 }
1208
1209 GOAT3DAPI int goat3d_set_node_name(struct goat3d_node *node, const char *name)
1210 {
1211         SETNAME(node->name, name);
1212 }
1213
1214 GOAT3DAPI const char *goat3d_get_node_name(const struct goat3d_node *node)
1215 {
1216         return node->name;
1217 }
1218
1219 GOAT3DAPI void goat3d_set_node_object(struct goat3d_node *node, enum goat3d_node_type type, void *obj)
1220 {
1221         node->obj = obj;
1222         node->type = type;
1223 }
1224
1225 GOAT3DAPI void *goat3d_get_node_object(const struct goat3d_node *node)
1226 {
1227         return node->obj;
1228 }
1229
1230 GOAT3DAPI enum goat3d_node_type goat3d_get_node_type(const struct goat3d_node *node)
1231 {
1232         return node->type;
1233 }
1234
1235 GOAT3DAPI void goat3d_add_node_child(struct goat3d_node *node, struct goat3d_node *child)
1236 {
1237         child->next = node->child;
1238         node->child = child;
1239         child->parent = node;
1240         node->child_count++;
1241
1242         child->matrix_valid = 0;
1243 }
1244
1245 GOAT3DAPI int goat3d_get_node_child_count(const struct goat3d_node *node)
1246 {
1247         return node->child_count;
1248 }
1249
1250 GOAT3DAPI struct goat3d_node *goat3d_get_node_child(const struct goat3d_node *node, int idx)
1251 {
1252         struct goat3d_node *c = node->child;
1253         while(c && idx-- > 0) {
1254                 c = c->next;
1255         }
1256         return c;
1257 }
1258
1259 GOAT3DAPI struct goat3d_node *goat3d_get_node_parent(const struct goat3d_node *node)
1260 {
1261         return node->parent;
1262 }
1263
1264 static void invalidate_subtree(struct goat3d_node *node)
1265 {
1266         struct goat3d_node *c = node->child;
1267
1268         while(c) {
1269                 invalidate_subtree(c);
1270                 c = c->next;
1271         }
1272         node->matrix_valid = 0;
1273 }
1274
1275
1276 GOAT3DAPI void goat3d_set_node_position(struct goat3d_node *node, float x, float y, float z)
1277 {
1278         cgm_vcons(&node->pos, x, y, z);
1279         invalidate_subtree(node);
1280 }
1281
1282 GOAT3DAPI void goat3d_set_node_rotation(struct goat3d_node *node, float qx, float qy, float qz, float qw)
1283 {
1284         cgm_qcons(&node->rot, qx, qy, qz, qw);
1285         invalidate_subtree(node);
1286 }
1287
1288 GOAT3DAPI void goat3d_set_node_scaling(struct goat3d_node *node, float sx, float sy, float sz)
1289 {
1290         cgm_vcons(&node->scale, sx, sy, sz);
1291         invalidate_subtree(node);
1292 }
1293
1294 GOAT3DAPI void goat3d_get_node_position(const struct goat3d_node *node, float *xptr, float *yptr, float *zptr)
1295 {
1296         if(node->has_anim) {
1297                 *xptr = node->apos.x;
1298                 *yptr = node->apos.y;
1299                 *zptr = node->apos.z;
1300         } else {
1301                 *xptr = node->pos.x;
1302                 *yptr = node->pos.y;
1303                 *zptr = node->pos.z;
1304         }
1305 }
1306
1307 GOAT3DAPI void goat3d_get_node_rotation(const struct goat3d_node *node, float *xptr, float *yptr, float *zptr, float *wptr)
1308 {
1309         if(node->has_anim) {
1310                 *xptr = node->arot.x;
1311                 *yptr = node->arot.y;
1312                 *zptr = node->arot.z;
1313                 *wptr = node->arot.w;
1314         } else {
1315                 *xptr = node->rot.x;
1316                 *yptr = node->rot.y;
1317                 *zptr = node->rot.z;
1318                 *wptr = node->rot.w;
1319         }
1320 }
1321
1322 GOAT3DAPI void goat3d_get_node_scaling(const struct goat3d_node *node, float *xptr, float *yptr, float *zptr)
1323 {
1324         if(node->has_anim) {
1325                 *xptr = node->ascale.x;
1326                 *yptr = node->ascale.y;
1327                 *zptr = node->ascale.z;
1328         } else {
1329                 *xptr = node->scale.x;
1330                 *yptr = node->scale.y;
1331                 *zptr = node->scale.z;
1332         }
1333 }
1334
1335
1336 GOAT3DAPI void goat3d_set_node_pivot(struct goat3d_node *node, float px, float py, float pz)
1337 {
1338         cgm_vcons(&node->pivot, px, py, pz);
1339         invalidate_subtree(node);
1340 }
1341
1342 GOAT3DAPI void goat3d_get_node_pivot(const struct goat3d_node *node, float *xptr, float *yptr, float *zptr)
1343 {
1344         *xptr = node->pivot.x;
1345         *yptr = node->pivot.y;
1346         *zptr = node->pivot.z;
1347 }
1348
1349 static void calc_node_matrix(const struct goat3d_node *node, float *mat)
1350 {
1351         int i;
1352         float rmat[16];
1353         cgm_vec3 pos, scale;
1354         cgm_quat rot;
1355
1356         if(node->has_anim) {
1357                 pos = node->apos;
1358                 rot = node->arot;
1359                 scale = node->ascale;
1360         } else {
1361                 pos = node->pos;
1362                 rot = node->rot;
1363                 scale = node->scale;
1364         }
1365
1366         cgm_mtranslation(mat, node->pivot.x, node->pivot.y, node->pivot.z);
1367         cgm_mrotation_quat(rmat, &rot);
1368
1369         for(i=0; i<3; i++) {
1370                 mat[i] = rmat[i];
1371                 mat[4 + i] = rmat[4 + i];
1372                 mat[8 + i] = rmat[8 + i];
1373         }
1374
1375         mat[0] *= scale.x; mat[4] *= scale.y; mat[8] *= scale.z; mat[12] += pos.x;
1376         mat[1] *= scale.x; mat[5] *= scale.y; mat[9] *= scale.z; mat[13] += pos.y;
1377         mat[2] *= scale.x; mat[6] *= scale.y; mat[10] *= scale.z; mat[14] += pos.z;
1378
1379         cgm_mpretranslate(mat, -node->pivot.x, -node->pivot.y, -node->pivot.z);
1380
1381         /* that's basically: pivot * rotation * translation * scaling * -pivot */
1382 }
1383
1384 GOAT3DAPI void goat3d_get_node_matrix(const struct goat3d_node *node, float *matrix)
1385 {
1386         if(!node->matrix_valid) {
1387                 calc_node_matrix(node, (float*)node->matrix);
1388                 ((struct goat3d_node*)node)->matrix_valid = 1;
1389         }
1390         memcpy(matrix, node->matrix, sizeof node->matrix);
1391 }
1392
1393 GOAT3DAPI void goat3d_get_matrix(const struct goat3d_node *node, float *matrix)
1394 {
1395         goat3d_get_node_matrix(node, matrix);
1396         if(node->parent) {
1397                 cgm_mmul(matrix, node->parent->matrix);
1398         }
1399 }
1400
1401 GOAT3DAPI void goat3d_get_node_bounds(const struct goat3d_node *node, float *bmin, float *bmax)
1402 {
1403         struct aabox box;
1404         g3dimpl_node_bounds(&box, (struct goat3d_node*)node);
1405
1406         bmin[0] = box.bmin.x;
1407         bmin[1] = box.bmin.y;
1408         bmin[2] = box.bmin.z;
1409         bmax[0] = box.bmax.x;
1410         bmax[1] = box.bmax.y;
1411         bmax[2] = box.bmax.z;
1412 }
1413
1414
1415 /* tracks */
1416 #define BASETYPE(type)  ((int)(type) & 0xff)
1417 static const int key_val_sz[] = {1, 3, 4, 4};
1418
1419 GOAT3DAPI struct goat3d_track *goat3d_create_track(void)
1420 {
1421         int i;
1422         struct goat3d_track *trk;
1423
1424         if(!(trk = calloc(1, sizeof *trk))) {
1425                 return 0;
1426         }
1427
1428         for(i=0; i<4; i++) {
1429                 if(anm_init_track(trk->trk + i) == -1) {
1430                         while(--i >= 0) {
1431                                 anm_destroy_track(trk->trk + i);
1432                         }
1433                         free(trk);
1434                         return 0;
1435                 }
1436         }
1437
1438         return trk;
1439 }
1440
1441 GOAT3DAPI void goat3d_destroy_track(struct goat3d_track *trk)
1442 {
1443         int i;
1444
1445         if(!trk) return;
1446
1447         free(trk->name);
1448
1449         for(i=0; i<4; i++) {
1450                 anm_destroy_track(trk->trk + i);
1451         }
1452 }
1453
1454 GOAT3DAPI int goat3d_set_track_name(struct goat3d_track *trk, const char *name)
1455 {
1456         SETNAME(trk->name, name);
1457 }
1458
1459 GOAT3DAPI const char *goat3d_get_track_name(const struct goat3d_track *trk)
1460 {
1461         return trk->name;
1462 }
1463
1464 GOAT3DAPI void goat3d_set_track_type(struct goat3d_track *trk, enum goat3d_track_type type)
1465 {
1466         trk->type = type;
1467
1468         switch(BASETYPE(type)) {
1469         case GOAT3D_TRACK_QUAT:
1470         case GOAT3D_TRACK_VEC4:
1471                 anm_set_track_default(trk->trk + 3, 1);
1472         case GOAT3D_TRACK_VEC3:
1473                 anm_set_track_default(trk->trk + 1, 0);
1474                 anm_set_track_default(trk->trk + 2, 0);
1475         case GOAT3D_TRACK_VAL:
1476                 anm_set_track_default(trk->trk + 0, 0);
1477         }
1478 }
1479
1480 GOAT3DAPI enum goat3d_track_type goat3d_get_track_type(const struct goat3d_track *trk)
1481 {
1482         return trk->type;
1483 }
1484
1485 GOAT3DAPI void goat3d_set_track_node(struct goat3d_track *trk, struct goat3d_node *node)
1486 {
1487         trk->node = node;
1488 }
1489
1490 GOAT3DAPI struct goat3d_node *goat3d_get_track_node(const struct goat3d_track *trk)
1491 {
1492         return trk->node;
1493 }
1494
1495 GOAT3DAPI void goat3d_set_track_interp(struct goat3d_track *trk, enum goat3d_interp in)
1496 {
1497         int i;
1498         for(i=0; i<4; i++) {
1499                 anm_set_track_interpolator(trk->trk + i, in);
1500         }
1501 }
1502
1503 GOAT3DAPI enum goat3d_interp goat3d_get_track_interp(const struct goat3d_track *trk)
1504 {
1505         return trk->trk[0].interp;
1506 }
1507
1508 GOAT3DAPI void goat3d_set_track_extrap(struct goat3d_track *trk, enum goat3d_extrap ex)
1509 {
1510         int i;
1511         for(i=0; i<4; i++) {
1512                 anm_set_track_extrapolator(trk->trk + i, ex);
1513         }
1514 }
1515
1516 GOAT3DAPI enum goat3d_extrap goat3d_get_track_extrap(const struct goat3d_track *trk)
1517 {
1518         return trk->trk[0].extrap;
1519 }
1520
1521 GOAT3DAPI int goat3d_set_track_key(struct goat3d_track *trk, const struct goat3d_key *key)
1522 {
1523         int i, num;
1524         enum goat3d_track_type basetype;
1525         long tm = ANM_MSEC2TM(key->tm);
1526
1527         basetype = BASETYPE(trk->type); /* e.g. ROT -> QUAT */
1528         num = key_val_sz[basetype];
1529
1530         for(i=0; i<num; i++) {
1531                 if(anm_set_value(trk->trk + i, tm, key->val[i]) == -1) {
1532                         return -1;
1533                 }
1534         }
1535         return 0;
1536 }
1537
1538 GOAT3DAPI int goat3d_get_track_key(const struct goat3d_track *trk, int idx, struct goat3d_key *key)
1539 {
1540         int i, num;
1541         struct anm_keyframe *akey;
1542         enum goat3d_track_type basetype;
1543
1544         basetype = BASETYPE(trk->type);
1545         num = key_val_sz[basetype];
1546
1547         for(i=0; i<num; i++) {
1548                 if(!(akey = anm_get_keyframe(trk->trk + i, idx))) {
1549                         return -1;
1550                 }
1551                 if(i == 0) {
1552                         key->tm = ANM_TM2MSEC(akey->time);
1553                 }
1554                 key->val[i] = akey->val;
1555         }
1556         return 0;
1557 }
1558
1559 GOAT3DAPI int goat3d_get_track_key_count(const struct goat3d_track *trk)
1560 {
1561         return trk->trk[0].count;
1562 }
1563
1564 GOAT3DAPI int goat3d_set_track_val(struct goat3d_track *trk, long msec, float val)
1565 {
1566         struct goat3d_key key = {0};
1567         enum goat3d_track_type basetype;
1568
1569         basetype = BASETYPE(trk->type);
1570
1571         if(basetype != GOAT3D_TRACK_VAL) {
1572                 goat3d_logmsg(LOG_WARNING, "goat3d_set_track_val called on %s track\n",
1573                                 g3dimpl_trktypestr(trk->type));
1574                 return -1;
1575         }
1576
1577         key.tm = msec;
1578         key.val[0] = val;
1579         return goat3d_set_track_key(trk, &key);
1580 }
1581
1582 GOAT3DAPI int goat3d_set_track_vec3(struct goat3d_track *trk, long msec, float x, float y, float z)
1583 {
1584         struct goat3d_key key = {0};
1585         enum goat3d_track_type basetype;
1586
1587         basetype = BASETYPE(trk->type);
1588
1589         if(basetype != GOAT3D_TRACK_VEC3) {
1590                 goat3d_logmsg(LOG_WARNING, "goat3d_set_track_vec3 called on %s track\n",
1591                                 g3dimpl_trktypestr(trk->type));
1592                 return -1;
1593         }
1594
1595         key.tm = msec;
1596         key.val[0] = x;
1597         key.val[1] = y;
1598         key.val[2] = z;
1599         return goat3d_set_track_key(trk, &key);
1600 }
1601
1602 GOAT3DAPI int goat3d_set_track_vec4(struct goat3d_track *trk, long msec, float x, float y, float z, float w)
1603 {
1604         struct goat3d_key key = {0};
1605         enum goat3d_track_type basetype;
1606
1607         basetype = BASETYPE(trk->type);
1608
1609         if(basetype != GOAT3D_TRACK_VEC4) {
1610                 goat3d_logmsg(LOG_WARNING, "goat3d_set_track_vec4 called on %s track\n",
1611                                 g3dimpl_trktypestr(trk->type));
1612                 return -1;
1613         }
1614
1615         key.tm = msec;
1616         key.val[0] = x;
1617         key.val[1] = y;
1618         key.val[2] = z;
1619         key.val[3] = w;
1620         return goat3d_set_track_key(trk, &key);
1621 }
1622
1623 GOAT3DAPI int goat3d_set_track_quat(struct goat3d_track *trk, long msec, float x, float y, float z, float w)
1624 {
1625         struct goat3d_key key = {0};
1626         enum goat3d_track_type basetype;
1627
1628         basetype = BASETYPE(trk->type);
1629
1630         if(basetype != GOAT3D_TRACK_QUAT) {
1631                 goat3d_logmsg(LOG_WARNING, "goat3d_set_track_quat called on %s track\n",
1632                                 g3dimpl_trktypestr(trk->type));
1633                 return -1;
1634         }
1635
1636         key.tm = msec;
1637         key.val[0] = x;
1638         key.val[1] = y;
1639         key.val[2] = z;
1640         key.val[3] = w;
1641         return goat3d_set_track_key(trk, &key);
1642 }
1643
1644
1645 GOAT3DAPI void goat3d_get_track_val(const struct goat3d_track *trk, long msec, float *valp)
1646 {
1647         enum goat3d_track_type basetype;
1648         anm_time_t tm = ANM_MSEC2TM(msec);
1649
1650         basetype = BASETYPE(trk->type);
1651
1652         if(basetype != GOAT3D_TRACK_VAL) {
1653                 goat3d_logmsg(LOG_WARNING, "goat3d_get_track_val called on %s track\n",
1654                                 g3dimpl_trktypestr(trk->type));
1655                 return;
1656         }
1657
1658         *valp = anm_get_value(trk->trk, tm);
1659 }
1660
1661 GOAT3DAPI void goat3d_get_track_vec3(const struct goat3d_track *trk, long msec, float *xp, float *yp, float *zp)
1662 {
1663         enum goat3d_track_type basetype;
1664         anm_time_t tm = ANM_MSEC2TM(msec);
1665
1666         basetype = BASETYPE(trk->type);
1667
1668         if(basetype != GOAT3D_TRACK_VEC3) {
1669                 goat3d_logmsg(LOG_WARNING, "goat3d_get_track_vec3 called on %s track\n",
1670                                 g3dimpl_trktypestr(trk->type));
1671                 return;
1672         }
1673
1674         *xp = anm_get_value(trk->trk, tm);
1675         *yp = anm_get_value(trk->trk + 1, tm);
1676         *zp = anm_get_value(trk->trk + 2, tm);
1677 }
1678
1679 GOAT3DAPI void goat3d_get_track_vec4(const struct goat3d_track *trk, long msec, float *xp, float *yp, float *zp, float *wp)
1680 {
1681         enum goat3d_track_type basetype;
1682         anm_time_t tm = ANM_MSEC2TM(msec);
1683
1684         basetype = BASETYPE(trk->type);
1685
1686         if(basetype != GOAT3D_TRACK_VEC4) {
1687                 goat3d_logmsg(LOG_WARNING, "goat3d_get_track_vec4 called on %s track\n",
1688                                 g3dimpl_trktypestr(trk->type));
1689                 return;
1690         }
1691
1692         *xp = anm_get_value(trk->trk, tm);
1693         *yp = anm_get_value(trk->trk + 1, tm);
1694         *zp = anm_get_value(trk->trk + 2, tm);
1695         *wp = anm_get_value(trk->trk + 3, tm);
1696 }
1697
1698 GOAT3DAPI void goat3d_get_track_quat(const struct goat3d_track *trk, long msec, float *xp, float *yp, float *zp, float *wp)
1699 {
1700         enum goat3d_track_type basetype;
1701         float quat[4];
1702         anm_time_t tm = ANM_MSEC2TM(msec);
1703
1704         basetype = BASETYPE(trk->type);
1705
1706         if(basetype != GOAT3D_TRACK_QUAT) {
1707                 goat3d_logmsg(LOG_WARNING, "goat3d_get_track_quat called on %s track\n",
1708                                 g3dimpl_trktypestr(trk->type));
1709                 return;
1710         }
1711
1712         anm_get_quat(trk->trk, trk->trk + 1, trk->trk + 2, trk->trk + 3, tm, quat);
1713         *xp = quat[0];
1714         *yp = quat[1];
1715         *zp = quat[2];
1716         *wp = quat[3];
1717 }
1718
1719 GOAT3DAPI long goat3d_get_track_timeline(const struct goat3d_track *trk, long *tstart, long *tend)
1720 {
1721         int i, j, num;
1722         enum goat3d_track_type basetype;
1723         struct anm_keyframe *key;
1724         anm_time_t start = ANM_TIME_MAX;
1725         anm_time_t end = ANM_TIME_MIN;
1726
1727         basetype = BASETYPE(trk->type);
1728         num = key_val_sz[basetype];
1729
1730         for(i=0; i<num; i++) {
1731                 for(j=0; j<trk->trk[i].count; j++) {
1732                         key = anm_get_keyframe(trk->trk + i, j);
1733                         if(key->time < start) start = key->time;
1734                         if(key->time > end) end = key->time;
1735                 }
1736         }
1737
1738         if(end < start) {
1739                 return -1;
1740         }
1741         *tstart = ANM_TM2MSEC(start);
1742         *tend = ANM_TM2MSEC(end);
1743         return *tend - *tstart;
1744 }
1745
1746 /* animation */
1747 GOAT3DAPI int goat3d_add_anim(struct goat3d *g, struct goat3d_anim *anim)
1748 {
1749         struct goat3d_anim **arr;
1750         if(!(arr = dynarr_push(g->anims, &anim))) {
1751                 return -1;
1752         }
1753         g->anims = arr;
1754         return 0;
1755 }
1756
1757 GOAT3DAPI int goat3d_get_anim_count(const struct goat3d *g)
1758 {
1759         return dynarr_size(g->anims);
1760 }
1761
1762 GOAT3DAPI struct goat3d_anim *goat3d_get_anim(const struct goat3d *g, int idx)
1763 {
1764         return g->anims[idx];
1765 }
1766
1767 GOAT3DAPI struct goat3d_anim *goat3d_get_anim_by_name(const struct goat3d *g, const char *name)
1768 {
1769         int i, num = dynarr_size(g->anims);
1770         for(i=0; i<num; i++) {
1771                 if(strcmp(g->anims[i]->name, name) == 0) {
1772                         return g->anims[i];
1773                 }
1774         }
1775         return 0;
1776 }
1777
1778 GOAT3DAPI struct goat3d_anim *goat3d_create_anim(void)
1779 {
1780         struct goat3d_anim *anim;
1781
1782         if(!(anim = malloc(sizeof *anim))) {
1783                 return 0;
1784         }
1785         if(g3dimpl_anim_init(anim) == -1) {
1786                 free(anim);
1787                 return 0;
1788         }
1789         return anim;
1790 }
1791
1792 GOAT3DAPI void goat3d_destroy_anim(struct goat3d_anim *anim)
1793 {
1794         g3dimpl_anim_destroy(anim);
1795         free(anim);
1796 }
1797
1798 GOAT3DAPI int goat3d_set_anim_name(struct goat3d_anim *anim, const char *name)
1799 {
1800         SETNAME(anim->name, name);
1801 }
1802
1803 GOAT3DAPI const char *goat3d_get_anim_name(const struct goat3d_anim *anim)
1804 {
1805         return anim->name;
1806 }
1807
1808 GOAT3DAPI int goat3d_add_anim_track(struct goat3d_anim *anim, struct goat3d_track *trk)
1809 {
1810         struct goat3d_track **tmptrk;
1811
1812         if(!(tmptrk = dynarr_push(anim->tracks, &trk))) {
1813                 return -1;
1814         }
1815         anim->tracks = tmptrk;
1816         return 0;
1817 }
1818
1819 GOAT3DAPI struct goat3d_track *goat3d_get_anim_track(const struct goat3d_anim *anim, int idx)
1820 {
1821         return anim->tracks[idx];
1822 }
1823
1824 GOAT3DAPI struct goat3d_track *goat3d_get_anim_track_by_name(const struct goat3d_anim *anim, const char *name)
1825 {
1826         int i, num = dynarr_size(anim->tracks);
1827         for(i=0; i<num; i++) {
1828                 if(strcmp(anim->tracks[i]->name, name) == 0) {
1829                         return anim->tracks[i];
1830                 }
1831         }
1832         return 0;
1833 }
1834
1835 GOAT3DAPI struct goat3d_track *goat3d_get_anim_track_by_type(const struct goat3d_anim *anim, enum goat3d_track_type type)
1836 {
1837         int i, num = dynarr_size(anim->tracks);
1838         for(i=0; i<num; i++) {
1839                 if(anim->tracks[i]->type == type) {
1840                         return anim->tracks[i];
1841                 }
1842         }
1843         return 0;
1844 }
1845
1846 GOAT3DAPI int goat3d_get_anim_track_count(const struct goat3d_anim *anim)
1847 {
1848         return dynarr_size(anim->tracks);
1849 }
1850
1851 GOAT3DAPI long goat3d_get_anim_timeline(const struct goat3d_anim *anim, long *tstart, long *tend)
1852 {
1853         int i, num = dynarr_size(anim->tracks);
1854         long start, end, trkstart, trkend;
1855
1856         start = LONG_MAX;
1857         end = LONG_MIN;
1858
1859         for(i=0; i<num; i++) {
1860                 if(goat3d_get_track_timeline(anim->tracks[i], &trkstart, &trkend) != -1) {
1861                         if(trkstart < start) start = trkstart;
1862                         if(trkend > end) end = trkend;
1863                 }
1864         }
1865
1866         if(end < start) {
1867                 return -1;
1868         }
1869
1870         *tstart = start;
1871         *tend = end;
1872         return end - start;
1873 }
1874
1875
1876
1877
1878 static long read_file(void *buf, size_t bytes, void *uptr)
1879 {
1880         return (long)fread(buf, 1, bytes, (FILE*)uptr);
1881 }
1882
1883 static long write_file(const void *buf, size_t bytes, void *uptr)
1884 {
1885         return (long)fwrite(buf, 1, bytes, (FILE*)uptr);
1886 }
1887
1888 static long seek_file(long offs, int whence, void *uptr)
1889 {
1890         if(fseek((FILE*)uptr, offs, whence) == -1) {
1891                 return -1;
1892         }
1893         return ftell((FILE*)uptr);
1894 }
1895
1896 static char *clean_filename(char *str)
1897 {
1898         char *last_slash, *ptr;
1899
1900         if(!(last_slash = strrchr(str, '/'))) {
1901                 last_slash = strrchr(str, '\\');
1902         }
1903         if(last_slash) {
1904                 str = last_slash + 1;
1905         }
1906
1907         ptr = str;
1908         while(*ptr) {
1909                 char c = tolower(*ptr);
1910                 *ptr++ = c;
1911         }
1912         *ptr = 0;
1913         return str;
1914 }