X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?a=blobdiff_plain;f=libs%2Fgoat3d%2Fsrc%2Fgoat3d.c;fp=libs%2Fgoat3d%2Fsrc%2Fgoat3d.c;h=9250b3ec9638d15b29be5a3ba1e14693ff025c79;hb=487d801f62b3f7cea009b5e560cfd06e57170970;hp=0000000000000000000000000000000000000000;hpb=103b67f9f1e041dc5f6ffbb59004ebe846e3f3f9;p=deeprace diff --git a/libs/goat3d/src/goat3d.c b/libs/goat3d/src/goat3d.c new file mode 100644 index 0000000..9250b3e --- /dev/null +++ b/libs/goat3d/src/goat3d.c @@ -0,0 +1,1914 @@ +/* +goat3d - 3D scene, and animation file format library. +Copyright (C) 2013-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include +#include +#include +#include "goat3d.h" +#include "g3dscn.h" +#include "log.h" +#include "dynarr.h" + +static long read_file(void *buf, size_t bytes, void *uptr); +static long write_file(const void *buf, size_t bytes, void *uptr); +static long seek_file(long offs, int whence, void *uptr); +static char *clean_filename(char *str); + +static const char *def_scn_name = "unnamed"; + +#define SETNAME(dest, str) \ + do { \ + char *tmpname; \ + int len = strlen(str); \ + if(!(tmpname = malloc(len + 1))) { \ + return -1; \ + } \ + memcpy(tmpname, str, len + 1); \ + free(dest); \ + dest = tmpname; \ + return 0; \ + } while(0) + + +GOAT3DAPI struct goat3d *goat3d_create(void) +{ + struct goat3d *g; + + if(!(g = malloc(sizeof *g))) { + return 0; + } + if(goat3d_init(g) == -1) { + free(g); + return 0; + } + return g; +} + +GOAT3DAPI void goat3d_free(struct goat3d *g) +{ + goat3d_destroy(g); + free(g); +} + +int goat3d_init(struct goat3d *g) +{ + memset(g, 0, sizeof *g); + + cgm_vcons(&g->ambient, 0.05, 0.05, 0.05); + + if(!(g->materials = dynarr_alloc(0, sizeof *g->materials))) goto err; + if(!(g->meshes = dynarr_alloc(0, sizeof *g->meshes))) goto err; + if(!(g->lights = dynarr_alloc(0, sizeof *g->lights))) goto err; + if(!(g->cameras = dynarr_alloc(0, sizeof *g->cameras))) goto err; + if(!(g->nodes = dynarr_alloc(0, sizeof *g->nodes))) goto err; + if(!(g->anims = dynarr_alloc(0, sizeof *g->anims))) goto err; + + return 0; + +err: + goat3d_destroy(g); + return -1; +} + +void goat3d_destroy(struct goat3d *g) +{ + goat3d_clear(g); + + dynarr_free(g->materials); + dynarr_free(g->meshes); + dynarr_free(g->lights); + dynarr_free(g->cameras); + dynarr_free(g->nodes); + dynarr_free(g->anims); +} + +void goat3d_clear(struct goat3d *g) +{ + int i, num; + + num = dynarr_size(g->materials); + for(i=0; imaterials[i]); + free(g->materials[i]); + } + DYNARR_CLEAR(g->materials); + + num = dynarr_size(g->meshes); + for(i=0; imeshes[i]); + free(g->meshes[i]); + } + DYNARR_CLEAR(g->meshes); + + num = dynarr_size(g->lights); + for(i=0; ilights[i]); + free(g->lights[i]); + } + DYNARR_CLEAR(g->lights); + + num = dynarr_size(g->cameras); + for(i=0; icameras[i]); + free(g->cameras[i]); + } + DYNARR_CLEAR(g->cameras); + + num = dynarr_size(g->nodes); + for(i=0; inodes[i]); + } + DYNARR_CLEAR(g->nodes); + + num = dynarr_size(g->anims); + for(i=0; ianims[i]); + } + + g->name = 0; + g->bbox_valid = 0; +} + +GOAT3DAPI void goat3d_setopt(struct goat3d *g, enum goat3d_option opt, int val) +{ + if(val) { + g->flags |= (1 << (int)opt); + } else { + g->flags &= ~(1 << (int)opt); + } +} + +GOAT3DAPI int goat3d_getopt(const struct goat3d *g, enum goat3d_option opt) +{ + return (g->flags >> (int)opt) & 1; +} + +GOAT3DAPI int goat3d_load(struct goat3d *g, const char *fname) +{ + int len, res; + char *slash; + FILE *fp = fopen(fname, "rb"); + if(!fp) { + goat3d_logmsg(LOG_ERROR, "failed to open file \"%s\" for reading: %s\n", fname, strerror(errno)); + return -1; + } + + /* if the filename contained any directory components, keep the prefix + * to use it as a search path for external mesh file loading + */ + len = strlen(fname); + if(!(g->search_path = malloc(len + 1))) { + fclose(fp); + return -1; + } + memcpy(g->search_path, fname, len + 1); + + if((slash = strrchr(g->search_path, '/'))) { + *slash = 0; + } else { + if((slash = strrchr(g->search_path, '\\'))) { + *slash = 0; + } else { + free(g->search_path); + g->search_path = 0; + } + } + + if((res = goat3d_load_file(g, fp)) == 0) { + const char *name; + if((name = goat3d_get_name(g)) == def_scn_name) { + goat3d_set_name(g, slash ? slash + 1 : fname); + } + } + fclose(fp); + return res; +} + +GOAT3DAPI int goat3d_save(const struct goat3d *g, const char *fname) +{ + int res; + FILE *fp = fopen(fname, "wb"); + if(!fp) { + goat3d_logmsg(LOG_ERROR, "failed to open file \"%s\" for writing: %s\n", fname, strerror(errno)); + return -1; + } + + res = goat3d_save_file(g, fp); + fclose(fp); + return res; +} + +GOAT3DAPI int goat3d_load_file(struct goat3d *g, FILE *fp) +{ + struct goat3d_io io; + io.cls = fp; + io.read = read_file; + io.write = write_file; + io.seek = seek_file; + + return goat3d_load_io(g, &io); +} + +GOAT3DAPI int goat3d_save_file(const struct goat3d *g, FILE *fp) +{ + struct goat3d_io io; + io.cls = fp; + io.read = read_file; + io.write = write_file; + io.seek = seek_file; + + return goat3d_save_io(g, &io); +} + +GOAT3DAPI int goat3d_load_io(struct goat3d *g, struct goat3d_io *io) +{ + return g3dimpl_scnload(g, io); +} + +GOAT3DAPI int goat3d_save_io(const struct goat3d *g, struct goat3d_io *io) +{ + if(goat3d_getopt(g, GOAT3D_OPT_SAVEXML)) { + goat3d_logmsg(LOG_ERROR, "saving in the original xml format is no longer supported\n"); + return -1; + } else if(goat3d_getopt(g, GOAT3D_OPT_SAVETEXT)) { + /* TODO set treestore output format as text */ + } + return g3dimpl_scnsave(g, io); +} + +/* save/load animations */ +GOAT3DAPI int goat3d_load_anim(struct goat3d *g, const char *fname) +{ + int res; + FILE *fp; + + if(!(fp = fopen(fname, "rb"))) { + return -1; + } + + res = goat3d_load_anim_file(g, fp); + fclose(fp); + return res; +} + +GOAT3DAPI int goat3d_save_anim(const struct goat3d *g, const char *fname) +{ + int res; + FILE *fp; + + if(!(fp = fopen(fname, "wb"))) { + return -1; + } + + res = goat3d_save_anim_file(g, fp); + fclose(fp); + return res; +} + +GOAT3DAPI int goat3d_load_anim_file(struct goat3d *g, FILE *fp) +{ + struct goat3d_io io; + io.cls = fp; + io.read = read_file; + io.write = write_file; + io.seek = seek_file; + + return goat3d_load_anim_io(g, &io); +} + +GOAT3DAPI int goat3d_save_anim_file(const struct goat3d *g, FILE *fp) +{ + struct goat3d_io io; + io.cls = fp; + io.read = read_file; + io.write = write_file; + io.seek = seek_file; + + return goat3d_save_anim_io(g, &io); +} + +GOAT3DAPI int goat3d_load_anim_io(struct goat3d *g, struct goat3d_io *io) +{ + return g3dimpl_anmload(g, io); +} + +GOAT3DAPI int goat3d_save_anim_io(const struct goat3d *g, struct goat3d_io *io) +{ + if(goat3d_getopt(g, GOAT3D_OPT_SAVEXML)) { + goat3d_logmsg(LOG_ERROR, "saving in the original xml format is no longer supported\n"); + return -1; + } else if(goat3d_getopt(g, GOAT3D_OPT_SAVETEXT)) { + /* TODO set treestore save format as text */ + } + return g3dimpl_anmsave(g, io); +} + + +GOAT3DAPI int goat3d_set_name(struct goat3d *g, const char *name) +{ + int len = strlen(name); + + free(g->name); + if(!(g->name = malloc(len + 1))) { + return -1; + } + memcpy(g->name, name, len + 1); + return 0; +} + +GOAT3DAPI const char *goat3d_get_name(const struct goat3d *g) +{ + return g->name ? g->name : def_scn_name; +} + +GOAT3DAPI void goat3d_set_ambient(struct goat3d *g, const float *amb) +{ + cgm_vcons(&g->ambient, amb[0], amb[1], amb[2]); +} + +GOAT3DAPI void goat3d_set_ambient3f(struct goat3d *g, float ar, float ag, float ab) +{ + cgm_vcons(&g->ambient, ar, ag, ab); +} + +GOAT3DAPI const float *goat3d_get_ambient(const struct goat3d *g) +{ + return &g->ambient.x; +} + +GOAT3DAPI int goat3d_get_bounds(const struct goat3d *g, float *bmin, float *bmax) +{ + int i, num_nodes, num_meshes; + struct aabox bbox; + + if(!g->bbox_valid) { + g3dimpl_aabox_init((struct aabox*)&g->bbox); + + if(dynarr_empty(g->nodes)) { +use_mesh_bounds: + num_meshes = dynarr_size(g->meshes); + for(i=0; imeshes[i], 0); + g3dimpl_aabox_union((struct aabox*)&g->bbox, &g->bbox, &bbox); + } + } else { + num_nodes = dynarr_size(g->nodes); + for(i=0; inodes[i]->parent) { + continue; + } + g3dimpl_node_bounds(&bbox, g->nodes[i]); + g3dimpl_aabox_union((struct aabox*)&g->bbox, &g->bbox, &bbox); + } + + /* in case the nodes are junk */ + if(g->bbox.bmin.x > g->bbox.bmax.x) { + goto use_mesh_bounds; + } + } + ((struct goat3d*)g)->bbox_valid = 1; + } + + if(g->bbox.bmin.x > g->bbox.bmax.x) { + return -1; + } + + bmin[0] = g->bbox.bmin.x; + bmin[1] = g->bbox.bmin.y; + bmin[2] = g->bbox.bmin.z; + bmax[0] = g->bbox.bmax.x; + bmax[1] = g->bbox.bmax.y; + bmax[2] = g->bbox.bmax.z; + return 0; +} + +// ---- materials ---- +GOAT3DAPI int goat3d_add_mtl(struct goat3d *g, struct goat3d_material *mtl) +{ + struct goat3d_material **newarr; + mtl->idx = dynarr_size(g->materials); + if(!(newarr = dynarr_push(g->materials, &mtl))) { + return -1; + } + g->materials = newarr; + return 0; +} + +GOAT3DAPI int goat3d_get_mtl_count(struct goat3d *g) +{ + return dynarr_size(g->materials); +} + +GOAT3DAPI struct goat3d_material *goat3d_get_mtl(struct goat3d *g, int idx) +{ + return g->materials[idx]; +} + +GOAT3DAPI struct goat3d_material *goat3d_get_mtl_by_name(struct goat3d *g, const char *name) +{ + int i, num = dynarr_size(g->materials); + for(i=0; imaterials[i]->name, name) == 0) { + return g->materials[i]; + } + } + return 0; +} + +GOAT3DAPI struct goat3d_material *goat3d_create_mtl(void) +{ + struct goat3d_material *mtl; + if(!(mtl = malloc(sizeof *mtl))) { + return 0; + } + g3dimpl_mtl_init(mtl); + return mtl; +} + +GOAT3DAPI void goat3d_destroy_mtl(struct goat3d_material *mtl) +{ + g3dimpl_mtl_destroy(mtl); + free(mtl); +} + +GOAT3DAPI int goat3d_set_mtl_name(struct goat3d_material *mtl, const char *name) +{ + SETNAME(mtl->name, name); +} + +GOAT3DAPI const char *goat3d_get_mtl_name(const struct goat3d_material *mtl) +{ + return mtl->name; +} + +GOAT3DAPI int goat3d_set_mtl_attrib(struct goat3d_material *mtl, const char *attrib, const float *val) +{ + struct material_attrib *ma = g3dimpl_mtl_getattr(mtl, attrib); + if(!ma) return -1; + cgm_wcons(&ma->value, val[0], val[1], val[2], val[3]); + return 0; +} + +GOAT3DAPI int goat3d_set_mtl_attrib1f(struct goat3d_material *mtl, const char *attrib, float val) +{ + return goat3d_set_mtl_attrib4f(mtl, attrib, val, 0, 0, 1); +} + +GOAT3DAPI int goat3d_set_mtl_attrib3f(struct goat3d_material *mtl, const char *attrib, float r, float g, float b) +{ + return goat3d_set_mtl_attrib4f(mtl, attrib, r, g, b, 1); +} + +GOAT3DAPI int goat3d_set_mtl_attrib4f(struct goat3d_material *mtl, const char *attrib, float r, float g, float b, float a) +{ + struct material_attrib *ma = g3dimpl_mtl_getattr(mtl, attrib); + if(!ma) return -1; + cgm_wcons(&ma->value, r, g, b, a); + return 0; +} + +GOAT3DAPI const float *goat3d_get_mtl_attrib(struct goat3d_material *mtl, const char *attrib) +{ + struct material_attrib *ma = g3dimpl_mtl_findattr(mtl, attrib); + return ma ? &ma->value.x : 0; +} + +GOAT3DAPI int goat3d_set_mtl_attrib_map(struct goat3d_material *mtl, const char *attrib, const char *mapname) +{ + int len; + char *tmp; + struct material_attrib *ma; + + len = strlen(mapname); + if(!(tmp = malloc(len + 1))) { + return -1; + } + memcpy(tmp, mapname, len + 1); + + if(!(ma = g3dimpl_mtl_getattr(mtl, attrib))) { + free(tmp); + return -1; + } + free(ma->map); + ma->map = tmp; + tmp = clean_filename(ma->map); + if(tmp != ma->map) { + memmove(ma->map, tmp, len - (tmp - ma->map) + 1); + } + return 0; +} + +GOAT3DAPI const char *goat3d_get_mtl_attrib_map(struct goat3d_material *mtl, const char *attrib) +{ + struct material_attrib *ma = g3dimpl_mtl_findattr(mtl, attrib); + return ma->map; +} + +GOAT3DAPI const char *goat3d_get_mtl_attrib_name(struct goat3d_material *mtl, int idx) +{ + return mtl->attrib[idx].name; +} + +GOAT3DAPI int goat3d_get_mtl_attrib_count(struct goat3d_material *mtl) +{ + return dynarr_size(mtl->attrib); +} + +// ---- meshes ---- +GOAT3DAPI int goat3d_add_mesh(struct goat3d *g, struct goat3d_mesh *mesh) +{ + struct goat3d_mesh **arr; + if(!(arr = dynarr_push(g->meshes, &mesh))) { + return -1; + } + g->meshes = arr; + return 0; +} + +GOAT3DAPI int goat3d_get_mesh_count(struct goat3d *g) +{ + return dynarr_size(g->meshes); +} + +GOAT3DAPI struct goat3d_mesh *goat3d_get_mesh(struct goat3d *g, int idx) +{ + return g->meshes[idx]; +} + +GOAT3DAPI struct goat3d_mesh *goat3d_get_mesh_by_name(struct goat3d *g, const char *name) +{ + int i, num = dynarr_size(g->meshes); + for(i=0; imeshes[i]->name, name) == 0) { + return g->meshes[i]; + } + } + return 0; +} + +GOAT3DAPI struct goat3d_mesh *goat3d_create_mesh(void) +{ + struct goat3d_mesh *m; + + if(!(m = malloc(sizeof *m))) { + return 0; + } + if(g3dimpl_obj_init((struct object*)m, OBJTYPE_MESH) == -1) { + free(m); + return 0; + } + return m; +} + +GOAT3DAPI void goat3d_destroy_mesh(struct goat3d_mesh *mesh) +{ + g3dimpl_obj_destroy((struct object*)mesh); + free(mesh); +} + +GOAT3DAPI int goat3d_set_mesh_name(struct goat3d_mesh *mesh, const char *name) +{ + SETNAME(mesh->name, name); +} + +GOAT3DAPI const char *goat3d_get_mesh_name(const struct goat3d_mesh *mesh) +{ + return mesh->name; +} + +GOAT3DAPI void goat3d_set_mesh_mtl(struct goat3d_mesh *mesh, struct goat3d_material *mtl) +{ + mesh->mtl = mtl; +} + +GOAT3DAPI struct goat3d_material *goat3d_get_mesh_mtl(struct goat3d_mesh *mesh) +{ + return mesh->mtl; +} + +GOAT3DAPI int goat3d_get_mesh_vertex_count(struct goat3d_mesh *mesh) +{ + return dynarr_size(mesh->vertices); +} + +GOAT3DAPI int goat3d_get_mesh_attrib_count(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib) +{ + switch(attrib) { + case GOAT3D_MESH_ATTR_VERTEX: + return dynarr_size(mesh->vertices); + case GOAT3D_MESH_ATTR_NORMAL: + return dynarr_size(mesh->normals); + case GOAT3D_MESH_ATTR_TANGENT: + return dynarr_size(mesh->tangents); + case GOAT3D_MESH_ATTR_TEXCOORD: + return dynarr_size(mesh->texcoords); + case GOAT3D_MESH_ATTR_SKIN_WEIGHT: + return dynarr_size(mesh->skin_weights); + case GOAT3D_MESH_ATTR_SKIN_MATRIX: + return dynarr_size(mesh->skin_matrices); + case GOAT3D_MESH_ATTR_COLOR: + return dynarr_size(mesh->colors); + default: + break; + } + return 0; +} + +GOAT3DAPI int goat3d_get_mesh_face_count(struct goat3d_mesh *mesh) +{ + return dynarr_size(mesh->faces); +} + +#define SET_VERTEX_DATA(arr, p, n) \ + do { \ + void *tmp = dynarr_resize(arr, n); \ + if(!tmp) { \ + goat3d_logmsg(LOG_ERROR, "failed to resize vertex array (%d)\n", n); \ + return -1; \ + } \ + arr = tmp; \ + memcpy(arr, p, n * sizeof *arr); \ + } while(0) + +GOAT3DAPI int goat3d_set_mesh_attribs(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib, const void *data, int vnum) +{ + if(attrib == GOAT3D_MESH_ATTR_VERTEX) { + SET_VERTEX_DATA(mesh->vertices, data, vnum); + return 0; + } + + if(vnum != dynarr_size(mesh->vertices)) { + goat3d_logmsg(LOG_ERROR, "trying to set mesh attrib data with number of elements different than the vertex array\n"); + return -1; + } + + switch(attrib) { + case GOAT3D_MESH_ATTR_NORMAL: + SET_VERTEX_DATA(mesh->normals, data, vnum); + break; + case GOAT3D_MESH_ATTR_TANGENT: + SET_VERTEX_DATA(mesh->tangents, data, vnum); + break; + case GOAT3D_MESH_ATTR_TEXCOORD: + SET_VERTEX_DATA(mesh->texcoords, data, vnum); + break; + case GOAT3D_MESH_ATTR_SKIN_WEIGHT: + SET_VERTEX_DATA(mesh->skin_weights, data, vnum); + break; + case GOAT3D_MESH_ATTR_SKIN_MATRIX: + SET_VERTEX_DATA(mesh->skin_matrices, data, vnum); + break; + case GOAT3D_MESH_ATTR_COLOR: + SET_VERTEX_DATA(mesh->colors, data, vnum); + default: + goat3d_logmsg(LOG_ERROR, "trying to set unknown vertex attrib: %d\n", attrib); + return -1; + } + return 0; +} + +GOAT3DAPI int goat3d_add_mesh_attrib1f(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib, + float val) +{ + return goat3d_add_mesh_attrib4f(mesh, attrib, val, 0, 0, 1); +} + +GOAT3DAPI int goat3d_add_mesh_attrib2f(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib, + float x, float y) +{ + return goat3d_add_mesh_attrib4f(mesh, attrib, x, y, 0, 1); +} + +GOAT3DAPI int goat3d_add_mesh_attrib3f(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib, + float x, float y, float z) +{ + return goat3d_add_mesh_attrib4f(mesh, attrib, x, y, z, 1); +} + +GOAT3DAPI int goat3d_add_mesh_attrib4f(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib, + float x, float y, float z, float w) +{ + float vec[4]; + int4 intvec; + void *tmp; + + switch(attrib) { + case GOAT3D_MESH_ATTR_VERTEX: + cgm_vcons((cgm_vec3*)vec, x, y, z); + if(!(tmp = dynarr_push(mesh->vertices, vec))) { + goto err; + } + mesh->vertices = tmp; + break; + + case GOAT3D_MESH_ATTR_NORMAL: + cgm_vcons((cgm_vec3*)vec, x, y, z); + if(!(tmp = dynarr_push(mesh->normals, vec))) { + goto err; + } + mesh->normals = tmp; + break; + + case GOAT3D_MESH_ATTR_TANGENT: + cgm_vcons((cgm_vec3*)vec, x, y, z); + if(!(tmp = dynarr_push(mesh->tangents, vec))) { + goto err; + } + mesh->tangents = tmp; + break; + + case GOAT3D_MESH_ATTR_TEXCOORD: + cgm_vcons((cgm_vec3*)vec, x, y, 0); + if(!(tmp = dynarr_push(mesh->texcoords, vec))) { + goto err; + } + mesh->texcoords = tmp; + break; + + case GOAT3D_MESH_ATTR_SKIN_WEIGHT: + cgm_wcons((cgm_vec4*)vec, x, y, z, w); + if(!(tmp = dynarr_push(mesh->skin_weights, vec))) { + goto err; + } + mesh->skin_weights = tmp; + break; + + case GOAT3D_MESH_ATTR_SKIN_MATRIX: + intvec.x = x; + intvec.y = y; + intvec.z = z; + intvec.w = w; + if(!(tmp = dynarr_push(mesh->skin_matrices, &intvec))) { + goto err; + } + mesh->skin_matrices = tmp; + break; + + case GOAT3D_MESH_ATTR_COLOR: + cgm_wcons((cgm_vec4*)vec, x, y, z, w); + if(!(tmp = dynarr_push(mesh->colors, vec))) { + goto err; + } + mesh->colors = tmp; + + default: + goat3d_logmsg(LOG_ERROR, "trying to add unknown vertex attrib: %d\n", attrib); + return -1; + } + return 0; + +err: + goat3d_logmsg(LOG_ERROR, "failed to push vertex attrib\n"); + return -1; +} + +GOAT3DAPI void *goat3d_get_mesh_attribs(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib) +{ + return goat3d_get_mesh_attrib(mesh, attrib, 0); +} + +GOAT3DAPI void *goat3d_get_mesh_attrib(struct goat3d_mesh *mesh, enum goat3d_mesh_attrib attrib, int idx) +{ + switch(attrib) { + case GOAT3D_MESH_ATTR_VERTEX: + return dynarr_empty(mesh->vertices) ? 0 : mesh->vertices + idx; + case GOAT3D_MESH_ATTR_NORMAL: + return dynarr_empty(mesh->normals) ? 0 : mesh->normals + idx; + case GOAT3D_MESH_ATTR_TANGENT: + return dynarr_empty(mesh->tangents) ? 0 : mesh->tangents + idx; + case GOAT3D_MESH_ATTR_TEXCOORD: + return dynarr_empty(mesh->texcoords) ? 0 : mesh->texcoords + idx; + case GOAT3D_MESH_ATTR_SKIN_WEIGHT: + return dynarr_empty(mesh->skin_weights) ? 0 : mesh->skin_weights + idx; + case GOAT3D_MESH_ATTR_SKIN_MATRIX: + return dynarr_empty(mesh->skin_matrices) ? 0 : mesh->skin_matrices + idx; + case GOAT3D_MESH_ATTR_COLOR: + return dynarr_empty(mesh->colors) ? 0 : mesh->colors + idx; + default: + break; + } + return 0; +} + + +GOAT3DAPI int goat3d_set_mesh_faces(struct goat3d_mesh *mesh, const int *data, int num) +{ + void *tmp; + if(!(tmp = dynarr_resize(mesh->faces, num))) { + goat3d_logmsg(LOG_ERROR, "failed to resize face array (%d)\n", num); + return -1; + } + mesh->faces = tmp; + memcpy(mesh->faces, data, num * sizeof *mesh->faces); + return 0; +} + +GOAT3DAPI int goat3d_add_mesh_face(struct goat3d_mesh *mesh, int a, int b, int c) +{ + void *tmp; + struct face face; + + face.v[0] = a; + face.v[1] = b; + face.v[2] = c; + + if(!(tmp = dynarr_push(mesh->faces, &face))) { + goat3d_logmsg(LOG_ERROR, "failed to add face\n"); + return -1; + } + mesh->faces = tmp; + return 0; +} + +GOAT3DAPI int *goat3d_get_mesh_faces(struct goat3d_mesh *mesh) +{ + return goat3d_get_mesh_face(mesh, 0); +} + +GOAT3DAPI int *goat3d_get_mesh_face(struct goat3d_mesh *mesh, int idx) +{ + return dynarr_empty(mesh->faces) ? 0 : mesh->faces[idx].v; +} + +// immedate mode state +static enum goat3d_im_primitive im_prim; +static struct goat3d_mesh *im_mesh; +static cgm_vec3 im_norm, im_tang; +static cgm_vec2 im_texcoord; +static cgm_vec4 im_skinw, im_color = {1, 1, 1, 1}; +static int4 im_skinmat; +static int im_use[NUM_GOAT3D_MESH_ATTRIBS]; + + +GOAT3DAPI void goat3d_begin(struct goat3d_mesh *mesh, enum goat3d_im_primitive prim) +{ + DYNARR_CLEAR(mesh->vertices); + DYNARR_CLEAR(mesh->normals); + DYNARR_CLEAR(mesh->tangents); + DYNARR_CLEAR(mesh->texcoords); + DYNARR_CLEAR(mesh->skin_weights); + DYNARR_CLEAR(mesh->skin_matrices); + DYNARR_CLEAR(mesh->colors); + DYNARR_CLEAR(mesh->faces); + + im_mesh = mesh; + memset(im_use, 0, sizeof im_use); + + im_prim = prim; +} + +GOAT3DAPI void goat3d_end(void) +{ + int i, vidx, num_faces, num_quads; + void *tmp; + + switch(im_prim) { + case GOAT3D_TRIANGLES: + { + num_faces = dynarr_size(im_mesh->vertices) / 3; + if(!(tmp = dynarr_resize(im_mesh->faces, num_faces))) { + return; + } + im_mesh->faces = tmp; + + vidx = 0; + for(i=0; ifaces[i].v[0] = vidx++; + im_mesh->faces[i].v[1] = vidx++; + im_mesh->faces[i].v[2] = vidx++; + } + } + break; + + case GOAT3D_QUADS: + { + num_quads = dynarr_size(im_mesh->vertices) / 4; + if(!(tmp = dynarr_resize(im_mesh->faces, num_quads * 2))) { + return; + } + im_mesh->faces = tmp; + + vidx = 0; + for(i=0; ifaces[i * 2].v[0] = vidx; + im_mesh->faces[i * 2].v[1] = vidx + 1; + im_mesh->faces[i * 2].v[2] = vidx + 2; + + im_mesh->faces[i * 2 + 1].v[0] = vidx; + im_mesh->faces[i * 2 + 1].v[1] = vidx + 2; + im_mesh->faces[i * 2 + 1].v[2] = vidx + 3; + + vidx += 4; + } + } + break; + + default: + break; + } +} + +GOAT3DAPI void goat3d_vertex3f(float x, float y, float z) +{ + void *tmp; + cgm_vec3 v; + + cgm_vcons(&v, x, y, z); + if(!(tmp = dynarr_push(im_mesh->vertices, &v))) { + return; + } + im_mesh->vertices = tmp; + + if(im_use[GOAT3D_MESH_ATTR_NORMAL]) { + if((tmp = dynarr_push(im_mesh->normals, &im_norm))) { + im_mesh->normals = tmp; + } + } + if(im_use[GOAT3D_MESH_ATTR_TANGENT]) { + if((tmp = dynarr_push(im_mesh->tangents, &im_tang))) { + im_mesh->tangents = tmp; + } + } + if(im_use[GOAT3D_MESH_ATTR_TEXCOORD]) { + if((tmp = dynarr_push(im_mesh->texcoords, &im_texcoord))) { + im_mesh->texcoords = tmp; + } + } + if(im_use[GOAT3D_MESH_ATTR_SKIN_WEIGHT]) { + if((tmp = dynarr_push(im_mesh->skin_weights, &im_skinw))) { + im_mesh->skin_weights = tmp; + } + } + if(im_use[GOAT3D_MESH_ATTR_SKIN_MATRIX]) { + if((tmp = dynarr_push(im_mesh->skin_matrices, &im_skinmat))) { + im_mesh->skin_matrices = tmp; + } + } + if(im_use[GOAT3D_MESH_ATTR_COLOR]) { + if((tmp = dynarr_push(im_mesh->colors, &im_color))) { + im_mesh->colors = tmp; + } + } +} + +GOAT3DAPI void goat3d_normal3f(float x, float y, float z) +{ + cgm_vcons(&im_norm, x, y, z); + im_use[GOAT3D_MESH_ATTR_NORMAL] = 1; +} + +GOAT3DAPI void goat3d_tangent3f(float x, float y, float z) +{ + cgm_vcons(&im_tang, x, y, z); + im_use[GOAT3D_MESH_ATTR_TANGENT] = 1; +} + +GOAT3DAPI void goat3d_texcoord2f(float x, float y) +{ + im_texcoord.x = x; + im_texcoord.y = y; + im_use[GOAT3D_MESH_ATTR_TEXCOORD] = 1; +} + +GOAT3DAPI void goat3d_skin_weight4f(float x, float y, float z, float w) +{ + cgm_wcons(&im_skinw, x, y, z, w); + im_use[GOAT3D_MESH_ATTR_SKIN_WEIGHT] = 1; +} + +GOAT3DAPI void goat3d_skin_matrix4i(int x, int y, int z, int w) +{ + im_skinmat.x = x; + im_skinmat.y = y; + im_skinmat.z = z; + im_skinmat.w = w; + im_use[GOAT3D_MESH_ATTR_SKIN_MATRIX] = 1; +} + +GOAT3DAPI void goat3d_color3f(float x, float y, float z) +{ + goat3d_color4f(x, y, z, 1.0f); +} + +GOAT3DAPI void goat3d_color4f(float x, float y, float z, float w) +{ + cgm_wcons(&im_color, x, y, z, w); + im_use[GOAT3D_MESH_ATTR_COLOR] = 1; +} + +GOAT3DAPI void goat3d_get_mesh_bounds(const struct goat3d_mesh *mesh, float *bmin, float *bmax) +{ + struct aabox box; + + g3dimpl_mesh_bounds(&box, (struct goat3d_mesh*)mesh, 0); + + bmin[0] = box.bmin.x; + bmin[1] = box.bmin.y; + bmin[2] = box.bmin.z; + bmax[0] = box.bmax.x; + bmax[1] = box.bmax.y; + bmax[2] = box.bmax.z; +} + +/* lights */ +GOAT3DAPI int goat3d_add_light(struct goat3d *g, struct goat3d_light *lt) +{ + struct goat3d_light **arr; + if(!(arr = dynarr_push(g->lights, <))) { + return -1; + } + g->lights = arr; + return 0; +} + +GOAT3DAPI int goat3d_get_light_count(struct goat3d *g) +{ + return dynarr_size(g->lights); +} + +GOAT3DAPI struct goat3d_light *goat3d_get_light(struct goat3d *g, int idx) +{ + return g->lights[idx]; +} + +GOAT3DAPI struct goat3d_light *goat3d_get_light_by_name(struct goat3d *g, const char *name) +{ + int i, num = dynarr_size(g->lights); + for(i=0; ilights[i]->name, name) == 0) { + return g->lights[i]; + } + } + return 0; +} + + +GOAT3DAPI struct goat3d_light *goat3d_create_light(void) +{ + struct goat3d_light *lt; + + if(!(lt = malloc(sizeof *lt))) { + return 0; + } + if(g3dimpl_obj_init((struct object*)lt, OBJTYPE_LIGHT) == -1) { + free(lt); + return 0; + } + return lt; +} + +GOAT3DAPI void goat3d_destroy_light(struct goat3d_light *lt) +{ + g3dimpl_obj_destroy((struct object*)lt); + free(lt); +} + +GOAT3DAPI int goat3d_set_light_name(struct goat3d_light *lt, const char *name) +{ + SETNAME(lt->name, name); +} + +GOAT3DAPI const char *goat3d_get_light_name(const struct goat3d_light *lt) +{ + return lt->name; +} + +/* cameras */ +GOAT3DAPI int goat3d_add_camera(struct goat3d *g, struct goat3d_camera *cam) +{ + struct goat3d_camera **arr; + if(!(arr = dynarr_push(g->cameras, &cam))) { + return -1; + } + g->cameras = arr; + return 0; +} + +GOAT3DAPI int goat3d_get_camera_count(struct goat3d *g) +{ + return dynarr_size(g->cameras); +} + +GOAT3DAPI struct goat3d_camera *goat3d_get_camera(struct goat3d *g, int idx) +{ + return g->cameras[idx]; +} + +GOAT3DAPI struct goat3d_camera *goat3d_get_camera_by_name(struct goat3d *g, const char *name) +{ + int i, num = dynarr_size(g->cameras); + for(i=0; icameras[i]->name, name) == 0) { + return g->cameras[i]; + } + } + return 0; +} + +GOAT3DAPI struct goat3d_camera *goat3d_create_camera(void) +{ + struct goat3d_camera *cam; + + if(!(cam = malloc(sizeof *cam))) { + return 0; + } + if(g3dimpl_obj_init((struct object*)cam, OBJTYPE_CAMERA) == -1) { + free(cam); + return 0; + } + return cam; +} + +GOAT3DAPI void goat3d_destroy_camera(struct goat3d_camera *cam) +{ + g3dimpl_obj_destroy((struct object*)cam); + free(cam); +} + +GOAT3DAPI int goat3d_set_camera_name(struct goat3d_camera *cam, const char *name) +{ + SETNAME(cam->name, name); +} + +GOAT3DAPI const char *goat3d_get_camera_name(const struct goat3d_camera *cam) +{ + return cam->name; +} + +/* node */ +GOAT3DAPI int goat3d_add_node(struct goat3d *g, struct goat3d_node *node) +{ + struct goat3d_node **arr; + if(!(arr = dynarr_push(g->nodes, &node))) { + return -1; + } + g->nodes = arr; + return 0; +} + +GOAT3DAPI int goat3d_get_node_count(struct goat3d *g) +{ + return dynarr_size(g->nodes); +} + +GOAT3DAPI struct goat3d_node *goat3d_get_node(struct goat3d *g, int idx) +{ + return g->nodes[idx]; +} + +GOAT3DAPI struct goat3d_node *goat3d_get_node_by_name(struct goat3d *g, const char *name) +{ + int i, num = dynarr_size(g->nodes); + for(i=0; inodes[i]->name, name) == 0) { + return g->nodes[i]; + } + } + return 0; +} + +GOAT3DAPI struct goat3d_node *goat3d_create_node(void) +{ + struct goat3d_node *node; + + if(!(node = calloc(1, sizeof *node))) { + return 0; + } + node->type = GOAT3D_NODE_NULL; + node->obj = 0; + node->child_count = 0; + + node->rot.w = node->arot.w = 1; + cgm_vcons(&node->scale, 1, 1, 1); + cgm_midentity(node->matrix); + + return node; +} + +GOAT3DAPI void goat3d_destroy_node(struct goat3d_node *node) +{ + if(!node) return; + free(node->name); + free(node); +} + +GOAT3DAPI int goat3d_set_node_name(struct goat3d_node *node, const char *name) +{ + SETNAME(node->name, name); +} + +GOAT3DAPI const char *goat3d_get_node_name(const struct goat3d_node *node) +{ + return node->name; +} + +GOAT3DAPI void goat3d_set_node_object(struct goat3d_node *node, enum goat3d_node_type type, void *obj) +{ + node->obj = obj; + node->type = type; +} + +GOAT3DAPI void *goat3d_get_node_object(const struct goat3d_node *node) +{ + return node->obj; +} + +GOAT3DAPI enum goat3d_node_type goat3d_get_node_type(const struct goat3d_node *node) +{ + return node->type; +} + +GOAT3DAPI void goat3d_add_node_child(struct goat3d_node *node, struct goat3d_node *child) +{ + child->next = node->child; + node->child = child; + child->parent = node; + node->child_count++; + + child->matrix_valid = 0; +} + +GOAT3DAPI int goat3d_get_node_child_count(const struct goat3d_node *node) +{ + return node->child_count; +} + +GOAT3DAPI struct goat3d_node *goat3d_get_node_child(const struct goat3d_node *node, int idx) +{ + struct goat3d_node *c = node->child; + while(c && idx-- > 0) { + c = c->next; + } + return c; +} + +GOAT3DAPI struct goat3d_node *goat3d_get_node_parent(const struct goat3d_node *node) +{ + return node->parent; +} + +static void invalidate_subtree(struct goat3d_node *node) +{ + struct goat3d_node *c = node->child; + + while(c) { + invalidate_subtree(c); + c = c->next; + } + node->matrix_valid = 0; +} + + +GOAT3DAPI void goat3d_set_node_position(struct goat3d_node *node, float x, float y, float z) +{ + cgm_vcons(&node->pos, x, y, z); + invalidate_subtree(node); +} + +GOAT3DAPI void goat3d_set_node_rotation(struct goat3d_node *node, float qx, float qy, float qz, float qw) +{ + cgm_qcons(&node->rot, qx, qy, qz, qw); + invalidate_subtree(node); +} + +GOAT3DAPI void goat3d_set_node_scaling(struct goat3d_node *node, float sx, float sy, float sz) +{ + cgm_vcons(&node->scale, sx, sy, sz); + invalidate_subtree(node); +} + +GOAT3DAPI void goat3d_get_node_position(const struct goat3d_node *node, float *xptr, float *yptr, float *zptr) +{ + if(node->has_anim) { + *xptr = node->apos.x; + *yptr = node->apos.y; + *zptr = node->apos.z; + } else { + *xptr = node->pos.x; + *yptr = node->pos.y; + *zptr = node->pos.z; + } +} + +GOAT3DAPI void goat3d_get_node_rotation(const struct goat3d_node *node, float *xptr, float *yptr, float *zptr, float *wptr) +{ + if(node->has_anim) { + *xptr = node->arot.x; + *yptr = node->arot.y; + *zptr = node->arot.z; + *wptr = node->arot.w; + } else { + *xptr = node->rot.x; + *yptr = node->rot.y; + *zptr = node->rot.z; + *wptr = node->rot.w; + } +} + +GOAT3DAPI void goat3d_get_node_scaling(const struct goat3d_node *node, float *xptr, float *yptr, float *zptr) +{ + if(node->has_anim) { + *xptr = node->ascale.x; + *yptr = node->ascale.y; + *zptr = node->ascale.z; + } else { + *xptr = node->scale.x; + *yptr = node->scale.y; + *zptr = node->scale.z; + } +} + + +GOAT3DAPI void goat3d_set_node_pivot(struct goat3d_node *node, float px, float py, float pz) +{ + cgm_vcons(&node->pivot, px, py, pz); + invalidate_subtree(node); +} + +GOAT3DAPI void goat3d_get_node_pivot(const struct goat3d_node *node, float *xptr, float *yptr, float *zptr) +{ + *xptr = node->pivot.x; + *yptr = node->pivot.y; + *zptr = node->pivot.z; +} + +static void calc_node_matrix(const struct goat3d_node *node, float *mat) +{ + int i; + float rmat[16]; + cgm_vec3 pos, scale; + cgm_quat rot; + + if(node->has_anim) { + pos = node->apos; + rot = node->arot; + scale = node->ascale; + } else { + pos = node->pos; + rot = node->rot; + scale = node->scale; + } + + cgm_mtranslation(mat, node->pivot.x, node->pivot.y, node->pivot.z); + cgm_mrotation_quat(rmat, &rot); + + for(i=0; i<3; i++) { + mat[i] = rmat[i]; + mat[4 + i] = rmat[4 + i]; + mat[8 + i] = rmat[8 + i]; + } + + mat[0] *= scale.x; mat[4] *= scale.y; mat[8] *= scale.z; mat[12] += pos.x; + mat[1] *= scale.x; mat[5] *= scale.y; mat[9] *= scale.z; mat[13] += pos.y; + mat[2] *= scale.x; mat[6] *= scale.y; mat[10] *= scale.z; mat[14] += pos.z; + + cgm_mpretranslate(mat, -node->pivot.x, -node->pivot.y, -node->pivot.z); + + /* that's basically: pivot * rotation * translation * scaling * -pivot */ +} + +GOAT3DAPI void goat3d_get_node_matrix(const struct goat3d_node *node, float *matrix) +{ + if(!node->matrix_valid) { + calc_node_matrix(node, (float*)node->matrix); + ((struct goat3d_node*)node)->matrix_valid = 1; + } + memcpy(matrix, node->matrix, sizeof node->matrix); +} + +GOAT3DAPI void goat3d_get_matrix(const struct goat3d_node *node, float *matrix) +{ + goat3d_get_node_matrix(node, matrix); + if(node->parent) { + cgm_mmul(matrix, node->parent->matrix); + } +} + +GOAT3DAPI void goat3d_get_node_bounds(const struct goat3d_node *node, float *bmin, float *bmax) +{ + struct aabox box; + g3dimpl_node_bounds(&box, (struct goat3d_node*)node); + + bmin[0] = box.bmin.x; + bmin[1] = box.bmin.y; + bmin[2] = box.bmin.z; + bmax[0] = box.bmax.x; + bmax[1] = box.bmax.y; + bmax[2] = box.bmax.z; +} + + +/* tracks */ +#define BASETYPE(type) ((int)(type) & 0xff) +static const int key_val_sz[] = {1, 3, 4, 4}; + +GOAT3DAPI struct goat3d_track *goat3d_create_track(void) +{ + int i; + struct goat3d_track *trk; + + if(!(trk = calloc(1, sizeof *trk))) { + return 0; + } + + for(i=0; i<4; i++) { + if(anm_init_track(trk->trk + i) == -1) { + while(--i >= 0) { + anm_destroy_track(trk->trk + i); + } + free(trk); + return 0; + } + } + + return trk; +} + +GOAT3DAPI void goat3d_destroy_track(struct goat3d_track *trk) +{ + int i; + + if(!trk) return; + + free(trk->name); + + for(i=0; i<4; i++) { + anm_destroy_track(trk->trk + i); + } +} + +GOAT3DAPI int goat3d_set_track_name(struct goat3d_track *trk, const char *name) +{ + SETNAME(trk->name, name); +} + +GOAT3DAPI const char *goat3d_get_track_name(const struct goat3d_track *trk) +{ + return trk->name; +} + +GOAT3DAPI void goat3d_set_track_type(struct goat3d_track *trk, enum goat3d_track_type type) +{ + trk->type = type; + + switch(BASETYPE(type)) { + case GOAT3D_TRACK_QUAT: + case GOAT3D_TRACK_VEC4: + anm_set_track_default(trk->trk + 3, 1); + case GOAT3D_TRACK_VEC3: + anm_set_track_default(trk->trk + 1, 0); + anm_set_track_default(trk->trk + 2, 0); + case GOAT3D_TRACK_VAL: + anm_set_track_default(trk->trk + 0, 0); + } +} + +GOAT3DAPI enum goat3d_track_type goat3d_get_track_type(const struct goat3d_track *trk) +{ + return trk->type; +} + +GOAT3DAPI void goat3d_set_track_node(struct goat3d_track *trk, struct goat3d_node *node) +{ + trk->node = node; +} + +GOAT3DAPI struct goat3d_node *goat3d_get_track_node(const struct goat3d_track *trk) +{ + return trk->node; +} + +GOAT3DAPI void goat3d_set_track_interp(struct goat3d_track *trk, enum goat3d_interp in) +{ + int i; + for(i=0; i<4; i++) { + anm_set_track_interpolator(trk->trk + i, in); + } +} + +GOAT3DAPI enum goat3d_interp goat3d_get_track_interp(const struct goat3d_track *trk) +{ + return trk->trk[0].interp; +} + +GOAT3DAPI void goat3d_set_track_extrap(struct goat3d_track *trk, enum goat3d_extrap ex) +{ + int i; + for(i=0; i<4; i++) { + anm_set_track_extrapolator(trk->trk + i, ex); + } +} + +GOAT3DAPI enum goat3d_extrap goat3d_get_track_extrap(const struct goat3d_track *trk) +{ + return trk->trk[0].extrap; +} + +GOAT3DAPI int goat3d_set_track_key(struct goat3d_track *trk, const struct goat3d_key *key) +{ + int i, num; + enum goat3d_track_type basetype; + long tm = ANM_MSEC2TM(key->tm); + + basetype = BASETYPE(trk->type); /* e.g. ROT -> QUAT */ + num = key_val_sz[basetype]; + + for(i=0; itrk + i, tm, key->val[i]) == -1) { + return -1; + } + } + return 0; +} + +GOAT3DAPI int goat3d_get_track_key(const struct goat3d_track *trk, int idx, struct goat3d_key *key) +{ + int i, num; + struct anm_keyframe *akey; + enum goat3d_track_type basetype; + + basetype = BASETYPE(trk->type); + num = key_val_sz[basetype]; + + for(i=0; itrk + i, idx))) { + return -1; + } + if(i == 0) { + key->tm = ANM_TM2MSEC(akey->time); + } + key->val[i] = akey->val; + } + return 0; +} + +GOAT3DAPI int goat3d_get_track_key_count(const struct goat3d_track *trk) +{ + return trk->trk[0].count; +} + +GOAT3DAPI int goat3d_set_track_val(struct goat3d_track *trk, long msec, float val) +{ + struct goat3d_key key = {0}; + enum goat3d_track_type basetype; + + basetype = BASETYPE(trk->type); + + if(basetype != GOAT3D_TRACK_VAL) { + goat3d_logmsg(LOG_WARNING, "goat3d_set_track_val called on %s track\n", + g3dimpl_trktypestr(trk->type)); + return -1; + } + + key.tm = msec; + key.val[0] = val; + return goat3d_set_track_key(trk, &key); +} + +GOAT3DAPI int goat3d_set_track_vec3(struct goat3d_track *trk, long msec, float x, float y, float z) +{ + struct goat3d_key key = {0}; + enum goat3d_track_type basetype; + + basetype = BASETYPE(trk->type); + + if(basetype != GOAT3D_TRACK_VEC3) { + goat3d_logmsg(LOG_WARNING, "goat3d_set_track_vec3 called on %s track\n", + g3dimpl_trktypestr(trk->type)); + return -1; + } + + key.tm = msec; + key.val[0] = x; + key.val[1] = y; + key.val[2] = z; + return goat3d_set_track_key(trk, &key); +} + +GOAT3DAPI int goat3d_set_track_vec4(struct goat3d_track *trk, long msec, float x, float y, float z, float w) +{ + struct goat3d_key key = {0}; + enum goat3d_track_type basetype; + + basetype = BASETYPE(trk->type); + + if(basetype != GOAT3D_TRACK_VEC4) { + goat3d_logmsg(LOG_WARNING, "goat3d_set_track_vec4 called on %s track\n", + g3dimpl_trktypestr(trk->type)); + return -1; + } + + key.tm = msec; + key.val[0] = x; + key.val[1] = y; + key.val[2] = z; + key.val[3] = w; + return goat3d_set_track_key(trk, &key); +} + +GOAT3DAPI int goat3d_set_track_quat(struct goat3d_track *trk, long msec, float x, float y, float z, float w) +{ + struct goat3d_key key = {0}; + enum goat3d_track_type basetype; + + basetype = BASETYPE(trk->type); + + if(basetype != GOAT3D_TRACK_QUAT) { + goat3d_logmsg(LOG_WARNING, "goat3d_set_track_quat called on %s track\n", + g3dimpl_trktypestr(trk->type)); + return -1; + } + + key.tm = msec; + key.val[0] = x; + key.val[1] = y; + key.val[2] = z; + key.val[3] = w; + return goat3d_set_track_key(trk, &key); +} + + +GOAT3DAPI void goat3d_get_track_val(const struct goat3d_track *trk, long msec, float *valp) +{ + enum goat3d_track_type basetype; + anm_time_t tm = ANM_MSEC2TM(msec); + + basetype = BASETYPE(trk->type); + + if(basetype != GOAT3D_TRACK_VAL) { + goat3d_logmsg(LOG_WARNING, "goat3d_get_track_val called on %s track\n", + g3dimpl_trktypestr(trk->type)); + return; + } + + *valp = anm_get_value(trk->trk, tm); +} + +GOAT3DAPI void goat3d_get_track_vec3(const struct goat3d_track *trk, long msec, float *xp, float *yp, float *zp) +{ + enum goat3d_track_type basetype; + anm_time_t tm = ANM_MSEC2TM(msec); + + basetype = BASETYPE(trk->type); + + if(basetype != GOAT3D_TRACK_VEC3) { + goat3d_logmsg(LOG_WARNING, "goat3d_get_track_vec3 called on %s track\n", + g3dimpl_trktypestr(trk->type)); + return; + } + + *xp = anm_get_value(trk->trk, tm); + *yp = anm_get_value(trk->trk + 1, tm); + *zp = anm_get_value(trk->trk + 2, tm); +} + +GOAT3DAPI void goat3d_get_track_vec4(const struct goat3d_track *trk, long msec, float *xp, float *yp, float *zp, float *wp) +{ + enum goat3d_track_type basetype; + anm_time_t tm = ANM_MSEC2TM(msec); + + basetype = BASETYPE(trk->type); + + if(basetype != GOAT3D_TRACK_VEC4) { + goat3d_logmsg(LOG_WARNING, "goat3d_get_track_vec4 called on %s track\n", + g3dimpl_trktypestr(trk->type)); + return; + } + + *xp = anm_get_value(trk->trk, tm); + *yp = anm_get_value(trk->trk + 1, tm); + *zp = anm_get_value(trk->trk + 2, tm); + *wp = anm_get_value(trk->trk + 3, tm); +} + +GOAT3DAPI void goat3d_get_track_quat(const struct goat3d_track *trk, long msec, float *xp, float *yp, float *zp, float *wp) +{ + enum goat3d_track_type basetype; + float quat[4]; + anm_time_t tm = ANM_MSEC2TM(msec); + + basetype = BASETYPE(trk->type); + + if(basetype != GOAT3D_TRACK_QUAT) { + goat3d_logmsg(LOG_WARNING, "goat3d_get_track_quat called on %s track\n", + g3dimpl_trktypestr(trk->type)); + return; + } + + anm_get_quat(trk->trk, trk->trk + 1, trk->trk + 2, trk->trk + 3, tm, quat); + *xp = quat[0]; + *yp = quat[1]; + *zp = quat[2]; + *wp = quat[3]; +} + +GOAT3DAPI long goat3d_get_track_timeline(const struct goat3d_track *trk, long *tstart, long *tend) +{ + int i, j, num; + enum goat3d_track_type basetype; + struct anm_keyframe *key; + anm_time_t start = ANM_TIME_MAX; + anm_time_t end = ANM_TIME_MIN; + + basetype = BASETYPE(trk->type); + num = key_val_sz[basetype]; + + for(i=0; itrk[i].count; j++) { + key = anm_get_keyframe(trk->trk + i, j); + if(key->time < start) start = key->time; + if(key->time > end) end = key->time; + } + } + + if(end < start) { + return -1; + } + *tstart = ANM_TM2MSEC(start); + *tend = ANM_TM2MSEC(end); + return *tend - *tstart; +} + +/* animation */ +GOAT3DAPI int goat3d_add_anim(struct goat3d *g, struct goat3d_anim *anim) +{ + struct goat3d_anim **arr; + if(!(arr = dynarr_push(g->anims, &anim))) { + return -1; + } + g->anims = arr; + return 0; +} + +GOAT3DAPI int goat3d_get_anim_count(const struct goat3d *g) +{ + return dynarr_size(g->anims); +} + +GOAT3DAPI struct goat3d_anim *goat3d_get_anim(const struct goat3d *g, int idx) +{ + return g->anims[idx]; +} + +GOAT3DAPI struct goat3d_anim *goat3d_get_anim_by_name(const struct goat3d *g, const char *name) +{ + int i, num = dynarr_size(g->anims); + for(i=0; ianims[i]->name, name) == 0) { + return g->anims[i]; + } + } + return 0; +} + +GOAT3DAPI struct goat3d_anim *goat3d_create_anim(void) +{ + struct goat3d_anim *anim; + + if(!(anim = malloc(sizeof *anim))) { + return 0; + } + if(g3dimpl_anim_init(anim) == -1) { + free(anim); + return 0; + } + return anim; +} + +GOAT3DAPI void goat3d_destroy_anim(struct goat3d_anim *anim) +{ + g3dimpl_anim_destroy(anim); + free(anim); +} + +GOAT3DAPI int goat3d_set_anim_name(struct goat3d_anim *anim, const char *name) +{ + SETNAME(anim->name, name); +} + +GOAT3DAPI const char *goat3d_get_anim_name(const struct goat3d_anim *anim) +{ + return anim->name; +} + +GOAT3DAPI int goat3d_add_anim_track(struct goat3d_anim *anim, struct goat3d_track *trk) +{ + struct goat3d_track **tmptrk; + + if(!(tmptrk = dynarr_push(anim->tracks, &trk))) { + return -1; + } + anim->tracks = tmptrk; + return 0; +} + +GOAT3DAPI struct goat3d_track *goat3d_get_anim_track(const struct goat3d_anim *anim, int idx) +{ + return anim->tracks[idx]; +} + +GOAT3DAPI struct goat3d_track *goat3d_get_anim_track_by_name(const struct goat3d_anim *anim, const char *name) +{ + int i, num = dynarr_size(anim->tracks); + for(i=0; itracks[i]->name, name) == 0) { + return anim->tracks[i]; + } + } + return 0; +} + +GOAT3DAPI struct goat3d_track *goat3d_get_anim_track_by_type(const struct goat3d_anim *anim, enum goat3d_track_type type) +{ + int i, num = dynarr_size(anim->tracks); + for(i=0; itracks[i]->type == type) { + return anim->tracks[i]; + } + } + return 0; +} + +GOAT3DAPI int goat3d_get_anim_track_count(const struct goat3d_anim *anim) +{ + return dynarr_size(anim->tracks); +} + +GOAT3DAPI long goat3d_get_anim_timeline(const struct goat3d_anim *anim, long *tstart, long *tend) +{ + int i, num = dynarr_size(anim->tracks); + long start, end, trkstart, trkend; + + start = LONG_MAX; + end = LONG_MIN; + + for(i=0; itracks[i], &trkstart, &trkend) != -1) { + if(trkstart < start) start = trkstart; + if(trkend > end) end = trkend; + } + } + + if(end < start) { + return -1; + } + + *tstart = start; + *tend = end; + return end - start; +} + + + + +static long read_file(void *buf, size_t bytes, void *uptr) +{ + return (long)fread(buf, 1, bytes, (FILE*)uptr); +} + +static long write_file(const void *buf, size_t bytes, void *uptr) +{ + return (long)fwrite(buf, 1, bytes, (FILE*)uptr); +} + +static long seek_file(long offs, int whence, void *uptr) +{ + if(fseek((FILE*)uptr, offs, whence) == -1) { + return -1; + } + return ftell((FILE*)uptr); +} + +static char *clean_filename(char *str) +{ + char *last_slash, *ptr; + + if(!(last_slash = strrchr(str, '/'))) { + last_slash = strrchr(str, '\\'); + } + if(last_slash) { + str = last_slash + 1; + } + + ptr = str; + while(*ptr) { + char c = tolower(*ptr); + *ptr++ = c; + } + *ptr = 0; + return str; +}