/* * GRacer * * Copyright (C) 1999 Takashi Matsuda * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ #include #include #include #include #include #include #include #include "gr_ac3d.h" #include "gr_memory.h" typedef enum { AC3D_TOKEN_INVALID = -1, AC3D_TOKEN_MATERIAL = 0, AC3D_TOKEN_OBJECT, AC3D_TOKEN_NAME, AC3D_TOKEN_DATA, AC3D_TOKEN_TEXTURE, AC3D_TOKEN_TEXREP, AC3D_TOKEN_ROT, AC3D_TOKEN_LOC, AC3D_TOKEN_URL, AC3D_TOKEN_NUMVERT, AC3D_TOKEN_NUMSURF, AC3D_TOKEN_SURF, AC3D_TOKEN_KIDS, AC3D_TOKEN_MAT, AC3D_TOKEN_REFS, AC3D_TOKEN_WORLD, AC3D_TOKEN_POLY, AC3D_TOKEN_GROUP, } TokenType; static struct { TokenType type; char *str; int len; } tokens[] = { {AC3D_TOKEN_MATERIAL, "MATERIAL", 8}, {AC3D_TOKEN_OBJECT, "OBJECT", 6}, {AC3D_TOKEN_NAME, "name", 4}, {AC3D_TOKEN_DATA, "data", 4}, {AC3D_TOKEN_TEXTURE, "texture", 7}, {AC3D_TOKEN_TEXREP, "texrep", 6}, {AC3D_TOKEN_ROT, "rot", 3}, {AC3D_TOKEN_LOC, "loc", 3}, {AC3D_TOKEN_URL, "url", 3}, {AC3D_TOKEN_NUMVERT, "numvert", 7}, {AC3D_TOKEN_NUMSURF, "numsurf", 7}, {AC3D_TOKEN_SURF, "SURF", 4}, {AC3D_TOKEN_KIDS, "kids", 4}, {AC3D_TOKEN_MAT, "mat", 3}, {AC3D_TOKEN_REFS, "refs", 4}, {AC3D_TOKEN_WORLD, "world", 5}, {AC3D_TOKEN_POLY, "poly", 4}, {AC3D_TOKEN_GROUP, "group", 5}, {AC3D_TOKEN_INVALID, NULL, 0}, }; static int next_line (char *data, int *index); static int find_token (char *data, int *index); static int next_word (char *data, int *index); static char* get_name (char *data, int *index); static Ac3dMaterial* read_material (char *data, int *index); static Ac3dSurface* read_surface (Ac3dObject *obj, char *data, int *index); static Ac3dObject* read_object (Ac3dScene *scene, char *data, int *index); static void ac3d_surface_free (Ac3dSurface *surf); static void ac3d_object_free (Ac3dObject *obj); static void ac3d_material_free (Ac3dMaterial *mat); static int find_token (char *data, int *index) { int i; int j; char c; i = *index; while (data[i] == ' ' || data[i] == '\t' || data[i] == '\n') i ++; if (data[i] != '\0') { for (j=0; tokens[j].str != NULL; j++) { if (!strncmp (data+i, tokens[j].str, tokens[j].len) && (c = data[i+tokens[j].len], c == ' ' || c == '\t' || c == '\n' || c == '\0')) { i += tokens[j].len; *index = i; return tokens[j].type; } } } return AC3D_TOKEN_INVALID; } static int next_line (char *data, int *index) { int i = *index; for (; data[i] != '\0' && data[i] != '\n'; i++) {} if (data[i] != 0) { i++; } *index = i; return i; } static int next_word (char *data, int *index) { int i = *index; while (data[i] != ' ' && data[i] != '\t' && data[i] != '\n') { i++; } while (data[i] == ' ' || data[i] == '\t' || data[i] == '\n') { i++; } *index = i; return i; } static char * get_name (char *data, int *index) { char *name = NULL; char *tmp; int i = *index; int len; if (data[i] == '"') { i++; tmp = strchr (data+i, '"'); len = tmp - (data + i); if (len > 0) { name = gr_new (char, len+1); strncpy (name, data+i, len); name[len] = '\0'; } i += len + 1; } else { tmp = strpbrk (data+i, " \t"); len = tmp - (data + i); name = gr_new (char, len+1); strncpy (name, data+i, len); name[len] = '\0'; i += len; } *index = i; return name; } static Ac3dMaterial * read_material (char *data, int *index) { Ac3dMaterial *mat; mat = gr_new (Ac3dMaterial, 1); if (!mat) return NULL; memset (mat, 0, sizeof(*mat)); mat->name = get_name (data, index); next_word (data, index); sscanf (data + *index, "rgb %f %f %f amb %f %f %f emis %f %f %f spec %f %f %f shi %d trans %f", mat->rgb+0, mat->rgb+1, mat->rgb+2, mat->amb+0, mat->amb+1, mat->amb+2, mat->ems+0, mat->ems+1, mat->ems+2, mat->spc+0, mat->spc+1, mat->spc+2, &mat->shi, &mat->trans); next_line (data, index); return mat; } static Ac3dSurface * read_surface (Ac3dObject *obj, char *data, int *index) { int j; int token; int type; Ac3dSurface *surf; surf = gr_new (Ac3dSurface, 1); if (!surf) return NULL; memset (surf, 0, sizeof (*surf)); next_word (data, index); sscanf (data + *index, "%x", &type); surf->type = type; next_line (data, index); while (1) { token = find_token (data, index); switch (token) { case AC3D_TOKEN_MAT: next_word (data, index); sscanf (data + *index, "%d", &(surf->mat)); next_line (data, index); break; case AC3D_TOKEN_REFS: next_word (data, index); sscanf (data + *index, "%d", &(surf->num_refs)); next_line (data, index); if (surf->num_refs > 0) { surf->refs = gr_new (Ac3dRef, surf->num_refs); for (j=0; jnum_refs; j++) { sscanf (data + *index, "%d %f %f", &surf->refs[j].ref, &surf->refs[j].s, &surf->refs[j].t); next_line (data, index); } } return surf; default: ac3d_surface_free (surf); return NULL; } } } static Ac3dObject * read_object (Ac3dScene *scene, char *data, int *index) { Ac3dObject *obj; int token; int j; char *tmp; obj = gr_new (Ac3dObject, 1); if (!obj) return NULL; memset (obj, 0, sizeof(*obj)); token = find_token (data, index); switch (token) { case AC3D_TOKEN_WORLD: obj->type = AC3D_OBJ_WORLD; break; case AC3D_TOKEN_POLY: obj->type = AC3D_OBJ_POLY; break; case AC3D_TOKEN_GROUP: obj->type = AC3D_OBJ_GROUP; break; default: free (obj); return NULL; } next_line (data, index); while (data[*index] != '\0') { token = find_token (data, index); switch (token) { case AC3D_TOKEN_NAME: next_word (data, index); obj->name = get_name (data, index); next_line (data, index); break; case AC3D_TOKEN_DATA: sscanf (data + *index, "%d", &(obj->data_size)); next_line (data, index); obj->data = gr_new (char, obj->data_size+1); memcpy (obj->data, data + *index, obj->data_size); next_line (data, index); break; case AC3D_TOKEN_TEXTURE: next_word (data, index); obj->tex_name = get_name (data, index); next_line (data, index); break; case AC3D_TOKEN_TEXREP: next_word (data, index); sscanf (data + *index, "%f %f", &obj->texrep[0], &obj->texrep[1]); obj->options |= AC3D_OPT_TEXREP; next_line (data, index); break; case AC3D_TOKEN_ROT: next_word (data, index); sscanf (data + *index, "%f %f %f %f %f %f %f %f %f", &obj->rot[0][0], &obj->rot[0][1], &obj->rot[0][2], &obj->rot[1][0], &obj->rot[1][1], &obj->rot[1][2], &obj->rot[2][0], &obj->rot[2][1], &obj->rot[2][2]); obj->options |= AC3D_OPT_ROT; next_line (data, index); break; case AC3D_TOKEN_LOC: sscanf (data + *index, "%f %f %f", &obj->loc[0], &obj->loc[1], &obj->loc[2]); obj->options |= AC3D_OPT_LOC; next_line (data, index); break; case AC3D_TOKEN_URL: next_word (data, index); tmp = get_name (data, index); free (tmp); next_line (data, index); break; case AC3D_TOKEN_NUMVERT: next_word (data, index); sscanf (data + *index, "%d", &(obj->num_verts)); next_line (data, index); if (obj->num_verts > 0) { obj->verts = gr_new (float, obj->num_verts * 3); for (j=0; jnum_verts; j++) { sscanf (data + *index, "%f %f %f", &X(obj->verts, j), &Y(obj->verts, j), &Z(obj->verts, j)); next_line (data, index); } } break; case AC3D_TOKEN_NUMSURF: next_word (data, index); sscanf (data + *index, "%d", &(obj->num_surfs)); next_line (data, index); if (obj->num_surfs > 0) { int j; obj->surfs = gr_new (Ac3dSurface *, obj->num_surfs); for (j=0; jnum_surfs; j++) { token = find_token (data, index); if (token != AC3D_TOKEN_SURF) goto ERROR; if ((obj->surfs[j] = read_surface (obj, data, index)) == NULL) { goto ERROR; } } } break; case AC3D_TOKEN_KIDS: next_word (data, index); sscanf (data + *index, "%d", &(obj->num_kids)); next_line (data, index); if (obj->num_kids > 0) { int j; obj->kids = gr_new (Ac3dObject *, obj->num_kids); for (j=0; jnum_kids; j++) { token = find_token (data, index); if (token != AC3D_TOKEN_OBJECT) goto ERROR; if (!(obj->kids[j] = read_object (scene, data, index))) { goto ERROR; } } } return obj; default: goto ERROR; } } ERROR: if (obj) { ac3d_object_free (obj); } return NULL; } Ac3dScene* ac3d_scene_new_from_file (FILE *file) { char *buf; size_t len; size_t pos; Ac3dScene *scene; pos = ftell (file); fseek (file, 0, SEEK_END); len = ftell (file) - pos; fseek (file, pos, SEEK_SET); buf = malloc (len+1); fread (buf, 1, len, file); buf[len] = '\0'; scene = ac3d_scene_new_from_data (buf); free (buf); return scene; } Ac3dScene * ac3d_scene_new_from_data (char *data) { int i = 0; int token; Ac3dScene *scene = NULL; int num_mats_max = 32; int num_objs_max = 32; Ac3dObject *obj; Ac3dMaterial *mat; if (!data) return NULL; if (strncmp (data, "AC3Db", 5)) goto ERROR; scene = malloc (sizeof(Ac3dScene)); if (!scene) goto ERROR; memset (scene, 0, sizeof (Ac3dScene)); scene->mats = gr_new (Ac3dMaterial *, num_mats_max); if (!scene->mats) goto ERROR; scene->objs = gr_new (Ac3dObject *, num_objs_max); if (!scene->objs) goto ERROR; next_line (data, &i); while (data[i] != '\0') { token = find_token (data, &i); switch (token) { case AC3D_TOKEN_MATERIAL: next_word (data, &i); mat = read_material (data, &i); if (mat) { if (scene->num_mats == num_mats_max) { num_mats_max *= 2; scene->mats = gr_renew (scene->mats, Ac3dMaterial *, num_mats_max); } scene->mats[scene->num_mats++] = mat; } break; case AC3D_TOKEN_OBJECT: next_word (data, &i); obj = read_object (scene, data, &i); if (obj) { if (scene->num_objs == num_objs_max) { num_objs_max *= 2; scene->objs = gr_renew (scene->objs, Ac3dObject *, num_objs_max); } scene->objs[scene->num_objs++] = obj; } break; default: goto ERROR; } } if (scene->num_mats == 0) { free (scene->mats); scene->mats = NULL; } else { scene->mats = gr_renew (scene->mats, Ac3dMaterial *, scene->num_mats); } if (scene->num_objs == 0) { free (scene->objs); scene->objs = NULL; } else { scene->objs = gr_renew (scene->objs, Ac3dObject *, scene->num_objs); } return scene; ERROR: if (scene) { if (scene->objs) { for (i=0; inum_objs; i++) { ac3d_object_free (scene->objs[i]); } free (scene->objs); } if (scene->mats) { for (i=0; inum_mats; i++) { ac3d_material_free (scene->mats[i]); } free (scene->mats); } free (scene); } return NULL; } static void ac3d_surface_free (Ac3dSurface *surf) { if (surf) { if (surf->refs) free (surf->refs); free (surf); } } static void ac3d_material_free (Ac3dMaterial *mat) { if (mat) { if (mat->name) free (mat->name); free (mat); } } static void ac3d_object_free (Ac3dObject *obj) { if (obj) { if (obj->name) free (obj->name); if (obj->data) free (obj->data); if (obj->tex_name) free (obj->tex_name); if (obj->url) free (obj->url); free (obj); } } static Ac3dObject * find_named_object (Ac3dObject *obj, char *name, Ac3dObject **from) { int i; Ac3dObject *tmp; if (obj == NULL) return NULL; if (*from == NULL && obj->name && !strcmp (obj->name, name)) return obj; if (*from == obj) *from = NULL; for (i=0; inum_kids; i++) { tmp = find_named_object (obj->kids[i], name, from); if (tmp != NULL) return tmp; } return NULL; } Ac3dObject * ac3d_scene_find_object (Ac3dScene *scene, char *name, Ac3dObject *from) { int i; Ac3dObject *obj; Ac3dObject *_from = from; if (scene == NULL || name == NULL) return NULL; for (i=0; inum_objs; i++) { obj = find_named_object (scene->objs[i], name, &_from); if (obj != NULL) return obj; } return NULL; } void ac3d_object_absolute_coord (Ac3dObject *object, float *origin) { float v[3]; int i; if (!object) return; if (origin) { v[0] = origin[0]; v[1] = origin[1]; v[2] = origin[2]; } else { v[0] = v[1] = v[2] = 0; } if (object->options & AC3D_OPT_LOC) { v[0] += object->loc[0]; v[1] += object->loc[1]; v[2] += object->loc[2]; } for (i=0; inum_verts; i++) { X(object->verts, i) += v[0]; Y(object->verts, i) += v[1]; Z(object->verts, i) += v[2]; } object->options &= ~AC3D_OPT_LOC; for (i=0; inum_kids; i++) ac3d_object_absolute_coord (object->kids[i], v); } void ac3d_object_scale (Ac3dObject *object, float *scale) { int i; if (!object || !scale) return; object->loc[0] *= scale[0]; object->loc[1] *= scale[1]; object->loc[2] *= scale[2]; for (i=0; inum_verts; i++) { X(object->verts, i) *= scale[0]; Y(object->verts, i) *= scale[1]; Z(object->verts, i) *= scale[2]; } for (i=0; inum_kids; i++) ac3d_object_scale (object->kids[i], scale); } void ac3d_object_convert_coord (Ac3dObject *obj) { int i; float f; f = obj->loc[0]; obj->loc[0] = -obj->loc[2]; obj->loc[2] = obj->loc[1]; obj->loc[1] = -f; for (i=0; inum_verts; i++) { f = X(obj->verts, i); X(obj->verts, i) = -Z(obj->verts, i); Z(obj->verts, i) = Y(obj->verts, i); Y(obj->verts, i) = -f; } for (i=0; inum_kids; i++) { ac3d_object_convert_coord (obj->kids[i]); } } static void print_material (Ac3dMaterial *mat, FILE *file) { fprintf (file, "MATERIAL \"%s\" rgb %f %f %f amb %f %f %f emis %f %f %f spec %f %f %f shi %d trans %f\n", mat->name? mat->name : "", mat->rgb[0], mat->rgb[1], mat->rgb[2], mat->amb[0], mat->amb[1], mat->amb[2], mat->ems[0], mat->ems[1], mat->ems[2], mat->spc[0], mat->spc[1], mat->spc[2], mat->shi, mat->trans); } static void print_surface (Ac3dSurface *surf, FILE *file) { int i; fprintf (file, "SURF 0x%x\n", surf->type); fprintf (file, "mat %d\n", surf->mat); fprintf (file, "refs %d\n", surf->num_refs); for (i=0; inum_refs; i++) fprintf (file, "%d %f %f\n", surf->refs[i].ref, surf->refs[i].s, surf->refs[i].t); } static void print_object (Ac3dObject *obj, FILE *file) { int i; fputs ("OBJECT ", file); switch (obj->type) { case AC3D_OBJ_WORLD: fputs ("world\n", file); break; case AC3D_OBJ_POLY: fputs ("poly\n", file); break; case AC3D_OBJ_GROUP: fputs ("group\n", file); break; } if (obj->name) fprintf (file, "name \"%s\"\n", obj->name); if (obj->data) { fprintf (file, "data %d\n", obj->data_size); fwrite (obj->data, 1, obj->data_size, file); fputc ('\n', file); } if (obj->tex_name) fprintf (file, "texture \"%s\"\n", obj->tex_name); if (obj->options & AC3D_OPT_TEXREP) fprintf (file, "texrep %f %f\n", obj->texrep[0], obj->texrep[1]); if (obj->options & AC3D_OPT_ROT) { fprintf (file, "rot %f %f %f %f %f %f %f %f %f\n", obj->rot[0][0], obj->rot[0][1], obj->rot[0][2], obj->rot[1][0], obj->rot[1][1], obj->rot[1][2], obj->rot[2][0], obj->rot[2][1], obj->rot[2][2]); } if (obj->options & AC3D_OPT_LOC) fprintf (file, "loc %f %f %f\n", obj->loc[0], obj->loc[1], obj->loc[2]); if (obj->url) fprintf (file, "url \"%s\"\n", obj->url); if (obj->num_verts) { fprintf (file, "numvert %d\n", obj->num_verts); for (i=0; inum_verts; i++) fprintf (file, "%f %f %f\n", X(obj->verts, i), Y(obj->verts, i), Z(obj->verts, i)); } if (obj->num_surfs) { fprintf (file, "numsurf %d\n", obj->num_surfs); for (i=0; inum_surfs; i++) print_surface (obj->surfs[i], file); } fprintf (file, "kids %d\n", obj->num_kids); for (i=0; inum_kids; i++) print_object (obj->kids[i], file); } void ac3d_write_scene (Ac3dScene *scene, FILE *file) { int i; if (!scene || !file) return; fputs ("AC3Db\n", file); for (i=0; inum_mats; i++) print_material (scene->mats[i], file); for (i=0; inum_objs; i++) print_object (scene->objs[i], file); }