2 goat3d - 3D scene, and animation file format library.
3 Copyright (C) 2013-2023 John Tsiombikas <nuclear@member.fsf.org>
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.
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.
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/>.
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);
31 static const char *def_scn_name = "unnamed";
33 #define SETNAME(dest, str) \
36 int len = strlen(str); \
37 if(!(tmpname = malloc(len + 1))) { \
40 memcpy(tmpname, str, len + 1); \
47 GOAT3DAPI struct goat3d *goat3d_create(void)
51 if(!(g = malloc(sizeof *g))) {
54 if(goat3d_init(g) == -1) {
61 GOAT3DAPI void goat3d_free(struct goat3d *g)
67 int goat3d_init(struct goat3d *g)
69 memset(g, 0, sizeof *g);
71 cgm_vcons(&g->ambient, 0.05, 0.05, 0.05);
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;
87 void goat3d_destroy(struct goat3d *g)
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);
99 void goat3d_clear(struct goat3d *g)
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]);
108 DYNARR_CLEAR(g->materials);
110 num = dynarr_size(g->meshes);
111 for(i=0; i<num; i++) {
112 g3dimpl_obj_destroy((struct object*)g->meshes[i]);
115 DYNARR_CLEAR(g->meshes);
117 num = dynarr_size(g->lights);
118 for(i=0; i<num; i++) {
119 g3dimpl_obj_destroy((struct object*)g->lights[i]);
122 DYNARR_CLEAR(g->lights);
124 num = dynarr_size(g->cameras);
125 for(i=0; i<num; i++) {
126 g3dimpl_obj_destroy((struct object*)g->cameras[i]);
129 DYNARR_CLEAR(g->cameras);
131 num = dynarr_size(g->nodes);
132 for(i=0; i<num; i++) {
133 goat3d_destroy_node(g->nodes[i]);
135 DYNARR_CLEAR(g->nodes);
137 num = dynarr_size(g->anims);
138 for(i=0; i<num; i++) {
139 g3dimpl_anim_destroy(g->anims[i]);
146 GOAT3DAPI void goat3d_setopt(struct goat3d *g, enum goat3d_option opt, int val)
149 g->flags |= (1 << (int)opt);
151 g->flags &= ~(1 << (int)opt);
155 GOAT3DAPI int goat3d_getopt(const struct goat3d *g, enum goat3d_option opt)
157 return (g->flags >> (int)opt) & 1;
160 GOAT3DAPI int goat3d_load(struct goat3d *g, const char *fname)
164 FILE *fp = fopen(fname, "rb");
166 goat3d_logmsg(LOG_ERROR, "failed to open file \"%s\" for reading: %s\n", fname, strerror(errno));
170 /* if the filename contained any directory components, keep the prefix
171 * to use it as a search path for external mesh file loading
174 if(!(g->search_path = malloc(len + 1))) {
178 memcpy(g->search_path, fname, len + 1);
180 if((slash = strrchr(g->search_path, '/'))) {
183 if((slash = strrchr(g->search_path, '\\'))) {
186 free(g->search_path);
191 if((res = goat3d_load_file(g, fp)) == 0) {
193 if((name = goat3d_get_name(g)) == def_scn_name) {
194 goat3d_set_name(g, slash ? slash + 1 : fname);
201 GOAT3DAPI int goat3d_save(const struct goat3d *g, const char *fname)
204 FILE *fp = fopen(fname, "wb");
206 goat3d_logmsg(LOG_ERROR, "failed to open file \"%s\" for writing: %s\n", fname, strerror(errno));
210 res = goat3d_save_file(g, fp);
215 GOAT3DAPI int goat3d_load_file(struct goat3d *g, FILE *fp)
220 io.write = write_file;
223 return goat3d_load_io(g, &io);
226 GOAT3DAPI int goat3d_save_file(const struct goat3d *g, FILE *fp)
231 io.write = write_file;
234 return goat3d_save_io(g, &io);
237 GOAT3DAPI int goat3d_load_io(struct goat3d *g, struct goat3d_io *io)
239 return g3dimpl_scnload(g, io);
242 GOAT3DAPI int goat3d_save_io(const struct goat3d *g, struct goat3d_io *io)
244 if(goat3d_getopt(g, GOAT3D_OPT_SAVEXML)) {
245 goat3d_logmsg(LOG_ERROR, "saving in the original xml format is no longer supported\n");
247 } else if(goat3d_getopt(g, GOAT3D_OPT_SAVETEXT)) {
248 /* TODO set treestore output format as text */
250 return g3dimpl_scnsave(g, io);
253 /* save/load animations */
254 GOAT3DAPI int goat3d_load_anim(struct goat3d *g, const char *fname)
259 if(!(fp = fopen(fname, "rb"))) {
263 res = goat3d_load_anim_file(g, fp);
268 GOAT3DAPI int goat3d_save_anim(const struct goat3d *g, const char *fname)
273 if(!(fp = fopen(fname, "wb"))) {
277 res = goat3d_save_anim_file(g, fp);
282 GOAT3DAPI int goat3d_load_anim_file(struct goat3d *g, FILE *fp)
287 io.write = write_file;
290 return goat3d_load_anim_io(g, &io);
293 GOAT3DAPI int goat3d_save_anim_file(const struct goat3d *g, FILE *fp)
298 io.write = write_file;
301 return goat3d_save_anim_io(g, &io);
304 GOAT3DAPI int goat3d_load_anim_io(struct goat3d *g, struct goat3d_io *io)
306 return g3dimpl_anmload(g, io);
309 GOAT3DAPI int goat3d_save_anim_io(const struct goat3d *g, struct goat3d_io *io)
311 if(goat3d_getopt(g, GOAT3D_OPT_SAVEXML)) {
312 goat3d_logmsg(LOG_ERROR, "saving in the original xml format is no longer supported\n");
314 } else if(goat3d_getopt(g, GOAT3D_OPT_SAVETEXT)) {
315 /* TODO set treestore save format as text */
317 return g3dimpl_anmsave(g, io);
321 GOAT3DAPI int goat3d_set_name(struct goat3d *g, const char *name)
323 int len = strlen(name);
326 if(!(g->name = malloc(len + 1))) {
329 memcpy(g->name, name, len + 1);
333 GOAT3DAPI const char *goat3d_get_name(const struct goat3d *g)
335 return g->name ? g->name : def_scn_name;
338 GOAT3DAPI void goat3d_set_ambient(struct goat3d *g, const float *amb)
340 cgm_vcons(&g->ambient, amb[0], amb[1], amb[2]);
343 GOAT3DAPI void goat3d_set_ambient3f(struct goat3d *g, float ar, float ag, float ab)
345 cgm_vcons(&g->ambient, ar, ag, ab);
348 GOAT3DAPI const float *goat3d_get_ambient(const struct goat3d *g)
350 return &g->ambient.x;
353 GOAT3DAPI int goat3d_get_bounds(const struct goat3d *g, float *bmin, float *bmax)
355 int i, num_nodes, num_meshes;
359 g3dimpl_aabox_init((struct aabox*)&g->bbox);
361 if(dynarr_empty(g->nodes)) {
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);
369 num_nodes = dynarr_size(g->nodes);
370 for(i=0; i<num_nodes; i++) {
371 if(g->nodes[i]->parent) {
374 g3dimpl_node_bounds(&bbox, g->nodes[i]);
375 g3dimpl_aabox_union((struct aabox*)&g->bbox, &g->bbox, &bbox);
378 /* in case the nodes are junk */
379 if(g->bbox.bmin.x > g->bbox.bmax.x) {
380 goto use_mesh_bounds;
383 ((struct goat3d*)g)->bbox_valid = 1;
386 if(g->bbox.bmin.x > g->bbox.bmax.x) {
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;
399 // ---- materials ----
400 GOAT3DAPI int goat3d_add_mtl(struct goat3d *g, struct goat3d_material *mtl)
402 struct goat3d_material **newarr;
403 mtl->idx = dynarr_size(g->materials);
404 if(!(newarr = dynarr_push(g->materials, &mtl))) {
407 g->materials = newarr;
411 GOAT3DAPI int goat3d_get_mtl_count(struct goat3d *g)
413 return dynarr_size(g->materials);
416 GOAT3DAPI struct goat3d_material *goat3d_get_mtl(struct goat3d *g, int idx)
418 return g->materials[idx];
421 GOAT3DAPI struct goat3d_material *goat3d_get_mtl_by_name(struct goat3d *g, const char *name)
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];
432 GOAT3DAPI struct goat3d_material *goat3d_create_mtl(void)
434 struct goat3d_material *mtl;
435 if(!(mtl = malloc(sizeof *mtl))) {
438 g3dimpl_mtl_init(mtl);
442 GOAT3DAPI void goat3d_destroy_mtl(struct goat3d_material *mtl)
444 g3dimpl_mtl_destroy(mtl);
448 GOAT3DAPI int goat3d_set_mtl_name(struct goat3d_material *mtl, const char *name)
450 SETNAME(mtl->name, name);
453 GOAT3DAPI const char *goat3d_get_mtl_name(const struct goat3d_material *mtl)
458 GOAT3DAPI int goat3d_set_mtl_attrib(struct goat3d_material *mtl, const char *attrib, const float *val)
460 struct material_attrib *ma = g3dimpl_mtl_getattr(mtl, attrib);
462 cgm_wcons(&ma->value, val[0], val[1], val[2], val[3]);
466 GOAT3DAPI int goat3d_set_mtl_attrib1f(struct goat3d_material *mtl, const char *attrib, float val)
468 return goat3d_set_mtl_attrib4f(mtl, attrib, val, 0, 0, 1);
471 GOAT3DAPI int goat3d_set_mtl_attrib3f(struct goat3d_material *mtl, const char *attrib, float r, float g, float b)
473 return goat3d_set_mtl_attrib4f(mtl, attrib, r, g, b, 1);
476 GOAT3DAPI int goat3d_set_mtl_attrib4f(struct goat3d_material *mtl, const char *attrib, float r, float g, float b, float a)
478 struct material_attrib *ma = g3dimpl_mtl_getattr(mtl, attrib);
480 cgm_wcons(&ma->value, r, g, b, a);
484 GOAT3DAPI const float *goat3d_get_mtl_attrib(struct goat3d_material *mtl, const char *attrib)
486 struct material_attrib *ma = g3dimpl_mtl_findattr(mtl, attrib);
487 return ma ? &ma->value.x : 0;
490 GOAT3DAPI int goat3d_set_mtl_attrib_map(struct goat3d_material *mtl, const char *attrib, const char *mapname)
494 struct material_attrib *ma;
496 len = strlen(mapname);
497 if(!(tmp = malloc(len + 1))) {
500 memcpy(tmp, mapname, len + 1);
502 if(!(ma = g3dimpl_mtl_getattr(mtl, attrib))) {
508 tmp = clean_filename(ma->map);
510 memmove(ma->map, tmp, len - (tmp - ma->map) + 1);
515 GOAT3DAPI const char *goat3d_get_mtl_attrib_map(struct goat3d_material *mtl, const char *attrib)
517 struct material_attrib *ma = g3dimpl_mtl_findattr(mtl, attrib);
521 GOAT3DAPI const char *goat3d_get_mtl_attrib_name(struct goat3d_material *mtl, int idx)
523 return mtl->attrib[idx].name;
526 GOAT3DAPI int goat3d_get_mtl_attrib_count(struct goat3d_material *mtl)
528 return dynarr_size(mtl->attrib);
532 GOAT3DAPI int goat3d_add_mesh(struct goat3d *g, struct goat3d_mesh *mesh)
534 struct goat3d_mesh **arr;
535 if(!(arr = dynarr_push(g->meshes, &mesh))) {
542 GOAT3DAPI int goat3d_get_mesh_count(struct goat3d *g)
544 return dynarr_size(g->meshes);
547 GOAT3DAPI struct goat3d_mesh *goat3d_get_mesh(struct goat3d *g, int idx)
549 return g->meshes[idx];
552 GOAT3DAPI struct goat3d_mesh *goat3d_get_mesh_by_name(struct goat3d *g, const char *name)
554 int i, num = dynarr_size(g->meshes);
555 for(i=0; i<num; i++) {
556 if(strcmp(g->meshes[i]->name, name) == 0) {
563 GOAT3DAPI struct goat3d_mesh *goat3d_create_mesh(void)
565 struct goat3d_mesh *m;
567 if(!(m = malloc(sizeof *m))) {
570 if(g3dimpl_obj_init((struct object*)m, OBJTYPE_MESH) == -1) {
577 GOAT3DAPI void goat3d_destroy_mesh(struct goat3d_mesh *mesh)
579 g3dimpl_obj_destroy((struct object*)mesh);
583 GOAT3DAPI int goat3d_set_mesh_name(struct goat3d_mesh *mesh, const char *name)
585 SETNAME(mesh->name, name);
588 GOAT3DAPI const char *goat3d_get_mesh_name(const struct goat3d_mesh *mesh)
593 GOAT3DAPI void goat3d_set_mesh_mtl(struct goat3d_mesh *mesh, struct goat3d_material *mtl)
598 GOAT3DAPI struct goat3d_material *goat3d_get_mesh_mtl(struct goat3d_mesh *mesh)
603 GOAT3DAPI int goat3d_get_mesh_vertex_count(struct goat3d_mesh *mesh)
605 return dynarr_size(mesh->vertices);
608 GOAT3DAPI int goat3d_get_mesh_attrib_count(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib 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);
631 GOAT3DAPI int goat3d_get_mesh_face_count(struct goat3d_mesh *mesh)
633 return dynarr_size(mesh->faces);
636 #define SET_VERTEX_DATA(arr, p, n) \
638 void *tmp = dynarr_resize(arr, n); \
640 goat3d_logmsg(LOG_ERROR, "failed to resize vertex array (%d)\n", n); \
644 memcpy(arr, p, n * sizeof *arr); \
647 GOAT3DAPI int goat3d_set_mesh_attribs(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib, const void *data, int vnum)
649 if(attrib == GOAT3D_MESH_ATTR_VERTEX) {
650 SET_VERTEX_DATA(mesh->vertices, data, vnum);
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");
660 case GOAT3D_MESH_ATTR_NORMAL:
661 SET_VERTEX_DATA(mesh->normals, data, vnum);
663 case GOAT3D_MESH_ATTR_TANGENT:
664 SET_VERTEX_DATA(mesh->tangents, data, vnum);
666 case GOAT3D_MESH_ATTR_TEXCOORD:
667 SET_VERTEX_DATA(mesh->texcoords, data, vnum);
669 case GOAT3D_MESH_ATTR_SKIN_WEIGHT:
670 SET_VERTEX_DATA(mesh->skin_weights, data, vnum);
672 case GOAT3D_MESH_ATTR_SKIN_MATRIX:
673 SET_VERTEX_DATA(mesh->skin_matrices, data, vnum);
675 case GOAT3D_MESH_ATTR_COLOR:
676 SET_VERTEX_DATA(mesh->colors, data, vnum);
678 goat3d_logmsg(LOG_ERROR, "trying to set unknown vertex attrib: %d\n", attrib);
684 GOAT3DAPI int goat3d_add_mesh_attrib1f(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib,
687 return goat3d_add_mesh_attrib4f(mesh, attrib, val, 0, 0, 1);
690 GOAT3DAPI int goat3d_add_mesh_attrib2f(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib,
693 return goat3d_add_mesh_attrib4f(mesh, attrib, x, y, 0, 1);
696 GOAT3DAPI int goat3d_add_mesh_attrib3f(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib,
697 float x, float y, float z)
699 return goat3d_add_mesh_attrib4f(mesh, attrib, x, y, z, 1);
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)
710 case GOAT3D_MESH_ATTR_VERTEX:
711 cgm_vcons((cgm_vec3*)vec, x, y, z);
712 if(!(tmp = dynarr_push(mesh->vertices, vec))) {
715 mesh->vertices = tmp;
718 case GOAT3D_MESH_ATTR_NORMAL:
719 cgm_vcons((cgm_vec3*)vec, x, y, z);
720 if(!(tmp = dynarr_push(mesh->normals, vec))) {
726 case GOAT3D_MESH_ATTR_TANGENT:
727 cgm_vcons((cgm_vec3*)vec, x, y, z);
728 if(!(tmp = dynarr_push(mesh->tangents, vec))) {
731 mesh->tangents = tmp;
734 case GOAT3D_MESH_ATTR_TEXCOORD:
735 cgm_vcons((cgm_vec3*)vec, x, y, 0);
736 if(!(tmp = dynarr_push(mesh->texcoords, vec))) {
739 mesh->texcoords = tmp;
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))) {
747 mesh->skin_weights = tmp;
750 case GOAT3D_MESH_ATTR_SKIN_MATRIX:
755 if(!(tmp = dynarr_push(mesh->skin_matrices, &intvec))) {
758 mesh->skin_matrices = tmp;
761 case GOAT3D_MESH_ATTR_COLOR:
762 cgm_wcons((cgm_vec4*)vec, x, y, z, w);
763 if(!(tmp = dynarr_push(mesh->colors, vec))) {
769 goat3d_logmsg(LOG_ERROR, "trying to add unknown vertex attrib: %d\n", attrib);
775 goat3d_logmsg(LOG_ERROR, "failed to push vertex attrib\n");
779 GOAT3DAPI void *goat3d_get_mesh_attribs(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib)
781 return goat3d_get_mesh_attrib(mesh, attrib, 0);
784 GOAT3DAPI void *goat3d_get_mesh_attrib(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib, int idx)
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;
808 GOAT3DAPI int goat3d_set_mesh_faces(struct goat3d_mesh *mesh, const int *data, int num)
811 if(!(tmp = dynarr_resize(mesh->faces, num))) {
812 goat3d_logmsg(LOG_ERROR, "failed to resize face array (%d)\n", num);
816 memcpy(mesh->faces, data, num * sizeof *mesh->faces);
820 GOAT3DAPI int goat3d_add_mesh_face(struct goat3d_mesh *mesh, int a, int b, int c)
829 if(!(tmp = dynarr_push(mesh->faces, &face))) {
830 goat3d_logmsg(LOG_ERROR, "failed to add face\n");
837 GOAT3DAPI int *goat3d_get_mesh_faces(struct goat3d_mesh *mesh)
839 return goat3d_get_mesh_face(mesh, 0);
842 GOAT3DAPI int *goat3d_get_mesh_face(struct goat3d_mesh *mesh, int idx)
844 return dynarr_empty(mesh->faces) ? 0 : mesh->faces[idx].v;
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];
857 GOAT3DAPI void goat3d_begin(struct goat3d_mesh *mesh, enum goat3d_im_primitive prim)
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);
869 memset(im_use, 0, sizeof im_use);
874 GOAT3DAPI void goat3d_end(void)
876 int i, vidx, num_faces, num_quads;
880 case GOAT3D_TRIANGLES:
882 num_faces = dynarr_size(im_mesh->vertices) / 3;
883 if(!(tmp = dynarr_resize(im_mesh->faces, num_faces))) {
886 im_mesh->faces = tmp;
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++;
899 num_quads = dynarr_size(im_mesh->vertices) / 4;
900 if(!(tmp = dynarr_resize(im_mesh->faces, num_quads * 2))) {
903 im_mesh->faces = tmp;
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;
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;
925 GOAT3DAPI void goat3d_vertex3f(float x, float y, float z)
930 cgm_vcons(&v, x, y, z);
931 if(!(tmp = dynarr_push(im_mesh->vertices, &v))) {
934 im_mesh->vertices = tmp;
936 if(im_use[GOAT3D_MESH_ATTR_NORMAL]) {
937 if((tmp = dynarr_push(im_mesh->normals, &im_norm))) {
938 im_mesh->normals = tmp;
941 if(im_use[GOAT3D_MESH_ATTR_TANGENT]) {
942 if((tmp = dynarr_push(im_mesh->tangents, &im_tang))) {
943 im_mesh->tangents = tmp;
946 if(im_use[GOAT3D_MESH_ATTR_TEXCOORD]) {
947 if((tmp = dynarr_push(im_mesh->texcoords, &im_texcoord))) {
948 im_mesh->texcoords = tmp;
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;
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;
961 if(im_use[GOAT3D_MESH_ATTR_COLOR]) {
962 if((tmp = dynarr_push(im_mesh->colors, &im_color))) {
963 im_mesh->colors = tmp;
968 GOAT3DAPI void goat3d_normal3f(float x, float y, float z)
970 cgm_vcons(&im_norm, x, y, z);
971 im_use[GOAT3D_MESH_ATTR_NORMAL] = 1;
974 GOAT3DAPI void goat3d_tangent3f(float x, float y, float z)
976 cgm_vcons(&im_tang, x, y, z);
977 im_use[GOAT3D_MESH_ATTR_TANGENT] = 1;
980 GOAT3DAPI void goat3d_texcoord2f(float x, float y)
984 im_use[GOAT3D_MESH_ATTR_TEXCOORD] = 1;
987 GOAT3DAPI void goat3d_skin_weight4f(float x, float y, float z, float w)
989 cgm_wcons(&im_skinw, x, y, z, w);
990 im_use[GOAT3D_MESH_ATTR_SKIN_WEIGHT] = 1;
993 GOAT3DAPI void goat3d_skin_matrix4i(int x, int y, int z, int w)
999 im_use[GOAT3D_MESH_ATTR_SKIN_MATRIX] = 1;
1002 GOAT3DAPI void goat3d_color3f(float x, float y, float z)
1004 goat3d_color4f(x, y, z, 1.0f);
1007 GOAT3DAPI void goat3d_color4f(float x, float y, float z, float w)
1009 cgm_wcons(&im_color, x, y, z, w);
1010 im_use[GOAT3D_MESH_ATTR_COLOR] = 1;
1013 GOAT3DAPI void goat3d_get_mesh_bounds(const struct goat3d_mesh *mesh, float *bmin, float *bmax)
1017 g3dimpl_mesh_bounds(&box, (struct goat3d_mesh*)mesh, 0);
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;
1028 GOAT3DAPI int goat3d_add_light(struct goat3d *g, struct goat3d_light *lt)
1030 struct goat3d_light **arr;
1031 if(!(arr = dynarr_push(g->lights, <))) {
1038 GOAT3DAPI int goat3d_get_light_count(struct goat3d *g)
1040 return dynarr_size(g->lights);
1043 GOAT3DAPI struct goat3d_light *goat3d_get_light(struct goat3d *g, int idx)
1045 return g->lights[idx];
1048 GOAT3DAPI struct goat3d_light *goat3d_get_light_by_name(struct goat3d *g, const char *name)
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];
1060 GOAT3DAPI struct goat3d_light *goat3d_create_light(void)
1062 struct goat3d_light *lt;
1064 if(!(lt = malloc(sizeof *lt))) {
1067 if(g3dimpl_obj_init((struct object*)lt, OBJTYPE_LIGHT) == -1) {
1074 GOAT3DAPI void goat3d_destroy_light(struct goat3d_light *lt)
1076 g3dimpl_obj_destroy((struct object*)lt);
1080 GOAT3DAPI int goat3d_set_light_name(struct goat3d_light *lt, const char *name)
1082 SETNAME(lt->name, name);
1085 GOAT3DAPI const char *goat3d_get_light_name(const struct goat3d_light *lt)
1091 GOAT3DAPI int goat3d_add_camera(struct goat3d *g, struct goat3d_camera *cam)
1093 struct goat3d_camera **arr;
1094 if(!(arr = dynarr_push(g->cameras, &cam))) {
1101 GOAT3DAPI int goat3d_get_camera_count(struct goat3d *g)
1103 return dynarr_size(g->cameras);
1106 GOAT3DAPI struct goat3d_camera *goat3d_get_camera(struct goat3d *g, int idx)
1108 return g->cameras[idx];
1111 GOAT3DAPI struct goat3d_camera *goat3d_get_camera_by_name(struct goat3d *g, const char *name)
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];
1122 GOAT3DAPI struct goat3d_camera *goat3d_create_camera(void)
1124 struct goat3d_camera *cam;
1126 if(!(cam = malloc(sizeof *cam))) {
1129 if(g3dimpl_obj_init((struct object*)cam, OBJTYPE_CAMERA) == -1) {
1136 GOAT3DAPI void goat3d_destroy_camera(struct goat3d_camera *cam)
1138 g3dimpl_obj_destroy((struct object*)cam);
1142 GOAT3DAPI int goat3d_set_camera_name(struct goat3d_camera *cam, const char *name)
1144 SETNAME(cam->name, name);
1147 GOAT3DAPI const char *goat3d_get_camera_name(const struct goat3d_camera *cam)
1153 GOAT3DAPI int goat3d_add_node(struct goat3d *g, struct goat3d_node *node)
1155 struct goat3d_node **arr;
1156 if(!(arr = dynarr_push(g->nodes, &node))) {
1163 GOAT3DAPI int goat3d_get_node_count(struct goat3d *g)
1165 return dynarr_size(g->nodes);
1168 GOAT3DAPI struct goat3d_node *goat3d_get_node(struct goat3d *g, int idx)
1170 return g->nodes[idx];
1173 GOAT3DAPI struct goat3d_node *goat3d_get_node_by_name(struct goat3d *g, const char *name)
1175 int i, num = dynarr_size(g->nodes);
1176 for(i=0; i<num; i++) {
1177 if(strcmp(g->nodes[i]->name, name) == 0) {
1184 GOAT3DAPI struct goat3d_node *goat3d_create_node(void)
1186 struct goat3d_node *node;
1188 if(!(node = calloc(1, sizeof *node))) {
1191 node->type = GOAT3D_NODE_NULL;
1193 node->child_count = 0;
1195 node->rot.w = node->arot.w = 1;
1196 cgm_vcons(&node->scale, 1, 1, 1);
1197 cgm_midentity(node->matrix);
1202 GOAT3DAPI void goat3d_destroy_node(struct goat3d_node *node)
1209 GOAT3DAPI int goat3d_set_node_name(struct goat3d_node *node, const char *name)
1211 SETNAME(node->name, name);
1214 GOAT3DAPI const char *goat3d_get_node_name(const struct goat3d_node *node)
1219 GOAT3DAPI void goat3d_set_node_object(struct goat3d_node *node, enum goat3d_node_type type, void *obj)
1225 GOAT3DAPI void *goat3d_get_node_object(const struct goat3d_node *node)
1230 GOAT3DAPI enum goat3d_node_type goat3d_get_node_type(const struct goat3d_node *node)
1235 GOAT3DAPI void goat3d_add_node_child(struct goat3d_node *node, struct goat3d_node *child)
1237 child->next = node->child;
1238 node->child = child;
1239 child->parent = node;
1240 node->child_count++;
1242 child->matrix_valid = 0;
1245 GOAT3DAPI int goat3d_get_node_child_count(const struct goat3d_node *node)
1247 return node->child_count;
1250 GOAT3DAPI struct goat3d_node *goat3d_get_node_child(const struct goat3d_node *node, int idx)
1252 struct goat3d_node *c = node->child;
1253 while(c && idx-- > 0) {
1259 GOAT3DAPI struct goat3d_node *goat3d_get_node_parent(const struct goat3d_node *node)
1261 return node->parent;
1264 static void invalidate_subtree(struct goat3d_node *node)
1266 struct goat3d_node *c = node->child;
1269 invalidate_subtree(c);
1272 node->matrix_valid = 0;
1276 GOAT3DAPI void goat3d_set_node_position(struct goat3d_node *node, float x, float y, float z)
1278 cgm_vcons(&node->pos, x, y, z);
1279 invalidate_subtree(node);
1282 GOAT3DAPI void goat3d_set_node_rotation(struct goat3d_node *node, float qx, float qy, float qz, float qw)
1284 cgm_qcons(&node->rot, qx, qy, qz, qw);
1285 invalidate_subtree(node);
1288 GOAT3DAPI void goat3d_set_node_scaling(struct goat3d_node *node, float sx, float sy, float sz)
1290 cgm_vcons(&node->scale, sx, sy, sz);
1291 invalidate_subtree(node);
1294 GOAT3DAPI void goat3d_get_node_position(const struct goat3d_node *node, float *xptr, float *yptr, float *zptr)
1296 if(node->has_anim) {
1297 *xptr = node->apos.x;
1298 *yptr = node->apos.y;
1299 *zptr = node->apos.z;
1301 *xptr = node->pos.x;
1302 *yptr = node->pos.y;
1303 *zptr = node->pos.z;
1307 GOAT3DAPI void goat3d_get_node_rotation(const struct goat3d_node *node, float *xptr, float *yptr, float *zptr, float *wptr)
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;
1315 *xptr = node->rot.x;
1316 *yptr = node->rot.y;
1317 *zptr = node->rot.z;
1318 *wptr = node->rot.w;
1322 GOAT3DAPI void goat3d_get_node_scaling(const struct goat3d_node *node, float *xptr, float *yptr, float *zptr)
1324 if(node->has_anim) {
1325 *xptr = node->ascale.x;
1326 *yptr = node->ascale.y;
1327 *zptr = node->ascale.z;
1329 *xptr = node->scale.x;
1330 *yptr = node->scale.y;
1331 *zptr = node->scale.z;
1336 GOAT3DAPI void goat3d_set_node_pivot(struct goat3d_node *node, float px, float py, float pz)
1338 cgm_vcons(&node->pivot, px, py, pz);
1339 invalidate_subtree(node);
1342 GOAT3DAPI void goat3d_get_node_pivot(const struct goat3d_node *node, float *xptr, float *yptr, float *zptr)
1344 *xptr = node->pivot.x;
1345 *yptr = node->pivot.y;
1346 *zptr = node->pivot.z;
1349 static void calc_node_matrix(const struct goat3d_node *node, float *mat)
1353 cgm_vec3 pos, scale;
1356 if(node->has_anim) {
1359 scale = node->ascale;
1363 scale = node->scale;
1366 cgm_mtranslation(mat, node->pivot.x, node->pivot.y, node->pivot.z);
1367 cgm_mrotation_quat(rmat, &rot);
1369 for(i=0; i<3; i++) {
1371 mat[4 + i] = rmat[4 + i];
1372 mat[8 + i] = rmat[8 + i];
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;
1379 cgm_mpretranslate(mat, -node->pivot.x, -node->pivot.y, -node->pivot.z);
1381 /* that's basically: pivot * rotation * translation * scaling * -pivot */
1384 GOAT3DAPI void goat3d_get_node_matrix(const struct goat3d_node *node, float *matrix)
1386 if(!node->matrix_valid) {
1387 calc_node_matrix(node, (float*)node->matrix);
1388 ((struct goat3d_node*)node)->matrix_valid = 1;
1390 memcpy(matrix, node->matrix, sizeof node->matrix);
1393 GOAT3DAPI void goat3d_get_matrix(const struct goat3d_node *node, float *matrix)
1395 goat3d_get_node_matrix(node, matrix);
1397 cgm_mmul(matrix, node->parent->matrix);
1401 GOAT3DAPI void goat3d_get_node_bounds(const struct goat3d_node *node, float *bmin, float *bmax)
1404 g3dimpl_node_bounds(&box, (struct goat3d_node*)node);
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;
1416 #define BASETYPE(type) ((int)(type) & 0xff)
1417 static const int key_val_sz[] = {1, 3, 4, 4};
1419 GOAT3DAPI struct goat3d_track *goat3d_create_track(void)
1422 struct goat3d_track *trk;
1424 if(!(trk = calloc(1, sizeof *trk))) {
1428 for(i=0; i<4; i++) {
1429 if(anm_init_track(trk->trk + i) == -1) {
1431 anm_destroy_track(trk->trk + i);
1441 GOAT3DAPI void goat3d_destroy_track(struct goat3d_track *trk)
1449 for(i=0; i<4; i++) {
1450 anm_destroy_track(trk->trk + i);
1454 GOAT3DAPI int goat3d_set_track_name(struct goat3d_track *trk, const char *name)
1456 SETNAME(trk->name, name);
1459 GOAT3DAPI const char *goat3d_get_track_name(const struct goat3d_track *trk)
1464 GOAT3DAPI void goat3d_set_track_type(struct goat3d_track *trk, enum goat3d_track_type type)
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);
1480 GOAT3DAPI enum goat3d_track_type goat3d_get_track_type(const struct goat3d_track *trk)
1485 GOAT3DAPI void goat3d_set_track_node(struct goat3d_track *trk, struct goat3d_node *node)
1490 GOAT3DAPI struct goat3d_node *goat3d_get_track_node(const struct goat3d_track *trk)
1495 GOAT3DAPI void goat3d_set_track_interp(struct goat3d_track *trk, enum goat3d_interp in)
1498 for(i=0; i<4; i++) {
1499 anm_set_track_interpolator(trk->trk + i, in);
1503 GOAT3DAPI enum goat3d_interp goat3d_get_track_interp(const struct goat3d_track *trk)
1505 return trk->trk[0].interp;
1508 GOAT3DAPI void goat3d_set_track_extrap(struct goat3d_track *trk, enum goat3d_extrap ex)
1511 for(i=0; i<4; i++) {
1512 anm_set_track_extrapolator(trk->trk + i, ex);
1516 GOAT3DAPI enum goat3d_extrap goat3d_get_track_extrap(const struct goat3d_track *trk)
1518 return trk->trk[0].extrap;
1521 GOAT3DAPI int goat3d_set_track_key(struct goat3d_track *trk, const struct goat3d_key *key)
1524 enum goat3d_track_type basetype;
1525 long tm = ANM_MSEC2TM(key->tm);
1527 basetype = BASETYPE(trk->type); /* e.g. ROT -> QUAT */
1528 num = key_val_sz[basetype];
1530 for(i=0; i<num; i++) {
1531 if(anm_set_value(trk->trk + i, tm, key->val[i]) == -1) {
1538 GOAT3DAPI int goat3d_get_track_key(const struct goat3d_track *trk, int idx, struct goat3d_key *key)
1541 struct anm_keyframe *akey;
1542 enum goat3d_track_type basetype;
1544 basetype = BASETYPE(trk->type);
1545 num = key_val_sz[basetype];
1547 for(i=0; i<num; i++) {
1548 if(!(akey = anm_get_keyframe(trk->trk + i, idx))) {
1552 key->tm = ANM_TM2MSEC(akey->time);
1554 key->val[i] = akey->val;
1559 GOAT3DAPI int goat3d_get_track_key_count(const struct goat3d_track *trk)
1561 return trk->trk[0].count;
1564 GOAT3DAPI int goat3d_set_track_val(struct goat3d_track *trk, long msec, float val)
1566 struct goat3d_key key = {0};
1567 enum goat3d_track_type basetype;
1569 basetype = BASETYPE(trk->type);
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));
1579 return goat3d_set_track_key(trk, &key);
1582 GOAT3DAPI int goat3d_set_track_vec3(struct goat3d_track *trk, long msec, float x, float y, float z)
1584 struct goat3d_key key = {0};
1585 enum goat3d_track_type basetype;
1587 basetype = BASETYPE(trk->type);
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));
1599 return goat3d_set_track_key(trk, &key);
1602 GOAT3DAPI int goat3d_set_track_vec4(struct goat3d_track *trk, long msec, float x, float y, float z, float w)
1604 struct goat3d_key key = {0};
1605 enum goat3d_track_type basetype;
1607 basetype = BASETYPE(trk->type);
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));
1620 return goat3d_set_track_key(trk, &key);
1623 GOAT3DAPI int goat3d_set_track_quat(struct goat3d_track *trk, long msec, float x, float y, float z, float w)
1625 struct goat3d_key key = {0};
1626 enum goat3d_track_type basetype;
1628 basetype = BASETYPE(trk->type);
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));
1641 return goat3d_set_track_key(trk, &key);
1645 GOAT3DAPI void goat3d_get_track_val(const struct goat3d_track *trk, long msec, float *valp)
1647 enum goat3d_track_type basetype;
1648 anm_time_t tm = ANM_MSEC2TM(msec);
1650 basetype = BASETYPE(trk->type);
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));
1658 *valp = anm_get_value(trk->trk, tm);
1661 GOAT3DAPI void goat3d_get_track_vec3(const struct goat3d_track *trk, long msec, float *xp, float *yp, float *zp)
1663 enum goat3d_track_type basetype;
1664 anm_time_t tm = ANM_MSEC2TM(msec);
1666 basetype = BASETYPE(trk->type);
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));
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);
1679 GOAT3DAPI void goat3d_get_track_vec4(const struct goat3d_track *trk, long msec, float *xp, float *yp, float *zp, float *wp)
1681 enum goat3d_track_type basetype;
1682 anm_time_t tm = ANM_MSEC2TM(msec);
1684 basetype = BASETYPE(trk->type);
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));
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);
1698 GOAT3DAPI void goat3d_get_track_quat(const struct goat3d_track *trk, long msec, float *xp, float *yp, float *zp, float *wp)
1700 enum goat3d_track_type basetype;
1702 anm_time_t tm = ANM_MSEC2TM(msec);
1704 basetype = BASETYPE(trk->type);
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));
1712 anm_get_quat(trk->trk, trk->trk + 1, trk->trk + 2, trk->trk + 3, tm, quat);
1719 GOAT3DAPI long goat3d_get_track_timeline(const struct goat3d_track *trk, long *tstart, long *tend)
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;
1727 basetype = BASETYPE(trk->type);
1728 num = key_val_sz[basetype];
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;
1741 *tstart = ANM_TM2MSEC(start);
1742 *tend = ANM_TM2MSEC(end);
1743 return *tend - *tstart;
1747 GOAT3DAPI int goat3d_add_anim(struct goat3d *g, struct goat3d_anim *anim)
1749 struct goat3d_anim **arr;
1750 if(!(arr = dynarr_push(g->anims, &anim))) {
1757 GOAT3DAPI int goat3d_get_anim_count(const struct goat3d *g)
1759 return dynarr_size(g->anims);
1762 GOAT3DAPI struct goat3d_anim *goat3d_get_anim(const struct goat3d *g, int idx)
1764 return g->anims[idx];
1767 GOAT3DAPI struct goat3d_anim *goat3d_get_anim_by_name(const struct goat3d *g, const char *name)
1769 int i, num = dynarr_size(g->anims);
1770 for(i=0; i<num; i++) {
1771 if(strcmp(g->anims[i]->name, name) == 0) {
1778 GOAT3DAPI struct goat3d_anim *goat3d_create_anim(void)
1780 struct goat3d_anim *anim;
1782 if(!(anim = malloc(sizeof *anim))) {
1785 if(g3dimpl_anim_init(anim) == -1) {
1792 GOAT3DAPI void goat3d_destroy_anim(struct goat3d_anim *anim)
1794 g3dimpl_anim_destroy(anim);
1798 GOAT3DAPI int goat3d_set_anim_name(struct goat3d_anim *anim, const char *name)
1800 SETNAME(anim->name, name);
1803 GOAT3DAPI const char *goat3d_get_anim_name(const struct goat3d_anim *anim)
1808 GOAT3DAPI int goat3d_add_anim_track(struct goat3d_anim *anim, struct goat3d_track *trk)
1810 struct goat3d_track **tmptrk;
1812 if(!(tmptrk = dynarr_push(anim->tracks, &trk))) {
1815 anim->tracks = tmptrk;
1819 GOAT3DAPI struct goat3d_track *goat3d_get_anim_track(const struct goat3d_anim *anim, int idx)
1821 return anim->tracks[idx];
1824 GOAT3DAPI struct goat3d_track *goat3d_get_anim_track_by_name(const struct goat3d_anim *anim, const char *name)
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];
1835 GOAT3DAPI struct goat3d_track *goat3d_get_anim_track_by_type(const struct goat3d_anim *anim, enum goat3d_track_type type)
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];
1846 GOAT3DAPI int goat3d_get_anim_track_count(const struct goat3d_anim *anim)
1848 return dynarr_size(anim->tracks);
1851 GOAT3DAPI long goat3d_get_anim_timeline(const struct goat3d_anim *anim, long *tstart, long *tend)
1853 int i, num = dynarr_size(anim->tracks);
1854 long start, end, trkstart, trkend;
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;
1878 static long read_file(void *buf, size_t bytes, void *uptr)
1880 return (long)fread(buf, 1, bytes, (FILE*)uptr);
1883 static long write_file(const void *buf, size_t bytes, void *uptr)
1885 return (long)fwrite(buf, 1, bytes, (FILE*)uptr);
1888 static long seek_file(long offs, int whence, void *uptr)
1890 if(fseek((FILE*)uptr, offs, whence) == -1) {
1893 return ftell((FILE*)uptr);
1896 static char *clean_filename(char *str)
1898 char *last_slash, *ptr;
1900 if(!(last_slash = strrchr(str, '/'))) {
1901 last_slash = strrchr(str, '\\');
1904 str = last_slash + 1;
1909 char c = tolower(*ptr);