/* * 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_memory.h" #include "gr_debug.h" #include "gr_scene.h" #include #include enum { SURF_POINTS = 0, SURF_LINES = 1, SURF_LINE_STRIP = 2, SURF_TRIANGLES = 3, SURF_TRIANGLE_STRIP = 4, SURF_TRIANGLE_FAN = 5, SURF_QUADS = 6, SURF_QUAD_STRIP = 7, SURF_POLYGON = 8, }; enum { SURF_SHADED = 1<<0, SURF_TWOSIDE = 1<<1, }; enum { AC3D_OPT_TEXREP = 1, AC3D_OPT_ROT = 2, AC3D_OPT_LOC = 4, }; enum { TOKEN_EOF = 0, TOKEN_NUMMAT, TOKEN_MAT, TOKEN_NUMOBJ, TOKEN_OBJ, TOKEN_TEX, TOKEN_ROT, TOKEN_LOC, TOKEN_URL, TOKEN_NUMVERT, TOKEN_NUMSURF, TOKEN_SURF, TOKEN_KIDS, TOKEN_NUMSEG, TOKEN_INVALID = -1, }; static const struct { int no; char *str; int len; } token_data[] = { { TOKEN_NUMMAT, "nummat", 6}, { TOKEN_MAT, "mat", 3}, { TOKEN_NUMOBJ, "numobj", 6}, { TOKEN_OBJ, "obj", 3}, { TOKEN_TEX, "texture", 7}, { TOKEN_ROT, "rot", 3}, { TOKEN_LOC, "loc", 3}, { TOKEN_URL, "url", 3}, { TOKEN_NUMVERT, "numvert", 7}, { TOKEN_NUMSURF, "numsurf", 7}, { TOKEN_SURF, "surf", 4}, { TOKEN_KIDS, "numkid", 6}, { TOKEN_NUMSEG, "numseg", 6}, { TOKEN_INVALID, NULL, -1}, }; 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 GrSurface* read_surface (GrObject *obj, char *data, int *index); static GrObject* read_object (GrScene *scene, char *data, int *index); 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') { return TOKEN_EOF; } else { for (j=0; token_data[j].str != NULL; j++) { if (!strncmp (data+i, token_data[j].str, token_data[j].len) && (c = data[i+token_data[j].len], c == ' ' || c == '\t' || c == '\n' || c == '\0')) { i += token_data[j].len; *index = i; return token_data[j].no; } } } return 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 void accumulate_normal (GrObject *obj, GrSurface *surf) { int i, p; for (i=0; inum_elements; i++) { p = surf->elements[i].ref; X(obj->normal, p) = surf->elements[i].n[0]; Y(obj->normal, p) = surf->elements[i].n[1]; Z(obj->normal, p) = surf->elements[i].n[2]; } } static void calc_normal_triangles (GrObject *obj, GrSurface *surf) { int i, j; GrVertex v, w, normal; int p0, p1, p2; if (surf->num_elements <= 2) return; for (i=0; inum_elements; i+=3) { p0 = surf->elements[i].ref; p1 = surf->elements[i+1].ref; p2 = surf->elements[i+2].ref; v.c.x = X(obj->vertex, p1) - X(obj->vertex, p0); v.c.y = Y(obj->vertex, p1) - Y(obj->vertex, p0); v.c.z = Z(obj->vertex, p1) - Z(obj->vertex, p0); w.c.x = X(obj->vertex, p2) - X(obj->vertex, p0); w.c.y = Y(obj->vertex, p2) - Y(obj->vertex, p0); w.c.z = Z(obj->vertex, p2) - Z(obj->vertex, p0); gr_vertex_normal (&normal, &v, &w); for (j=0; j<3; j++) { surf->elements[i+j].n[0] = normal.c.x; surf->elements[i+j].n[1] = normal.c.y; surf->elements[i+j].n[2] = normal.c.z; } } if (surf->smooth) accumulate_normal (obj, surf); } static void calc_normal_triangle_strip (GrObject *obj, GrSurface *surf) { int i; GrVertex v, w, normal; int p0, p1, p2; if (surf->num_elements <= 2) return; for (i=2; inum_elements; i++) { p0 = surf->elements[i-2].ref; p1 = surf->elements[i-1].ref; p2 = surf->elements[i-0].ref; v.c.x = X(obj->vertex, p1) - X(obj->vertex, p0); v.c.y = Y(obj->vertex, p1) - Y(obj->vertex, p0); v.c.z = Z(obj->vertex, p1) - Z(obj->vertex, p0); w.c.x = X(obj->vertex, p2) - X(obj->vertex, p0); w.c.y = Y(obj->vertex, p2) - Y(obj->vertex, p0); w.c.z = Z(obj->vertex, p2) - Z(obj->vertex, p0); if (i&1) { gr_vertex_normal (&normal, &w, &v); } else { gr_vertex_normal (&normal, &v, &w); } surf->elements[i].n[0] = normal.c.x; surf->elements[i].n[1] = normal.c.y; surf->elements[i].n[2] = normal.c.z; } for (i=0; i<2; i++) { surf->elements[i].n[0] = surf->elements[2].n[0]; surf->elements[i].n[1] = surf->elements[2].n[1]; surf->elements[i].n[2] = surf->elements[2].n[2]; } if (surf->smooth) accumulate_normal (obj, surf); } static void calc_normal_triangle_fan (GrObject *obj, GrSurface *surf) { int i; GrVertex v, w, normal; int p0, p1, p2; if (surf->num_elements <= 2) return; for (i=2; inum_elements; i++) { p0 = surf->elements[0].ref; p1 = surf->elements[i-1].ref; p2 = surf->elements[i-0].ref; v.c.x = X(obj->vertex, p1) - X(obj->vertex, p0); v.c.y = Y(obj->vertex, p1) - Y(obj->vertex, p0); v.c.z = Z(obj->vertex, p1) - Z(obj->vertex, p0); w.c.x = X(obj->vertex, p2) - X(obj->vertex, p0); w.c.y = Y(obj->vertex, p2) - Y(obj->vertex, p0); w.c.z = Z(obj->vertex, p2) - Z(obj->vertex, p0); gr_vertex_normal (&normal, &v, &w); surf->elements[i].n[0] = normal.c.x; surf->elements[i].n[1] = normal.c.y; surf->elements[i].n[2] = normal.c.z; } for (i=0; i<2; i++) { surf->elements[i].n[0] = surf->elements[2].n[0]; surf->elements[i].n[1] = surf->elements[2].n[1]; surf->elements[i].n[2] = surf->elements[2].n[2]; } if (surf->smooth) accumulate_normal (obj, surf); } static void calc_normal_quads (GrObject *obj, GrSurface *surf) { int i, j; GrVertex v, w, normal; int p0, p1, p2; if (surf->num_elements <= 2) return; for (i=0; inum_elements; i+=4) { p0 = surf->elements[i].ref; p1 = surf->elements[i+1].ref; p2 = surf->elements[i+2].ref; v.c.x = X(obj->vertex, p1) - X(obj->vertex, p0); v.c.y = Y(obj->vertex, p1) - Y(obj->vertex, p0); v.c.z = Z(obj->vertex, p1) - Z(obj->vertex, p0); w.c.x = X(obj->vertex, p2) - X(obj->vertex, p0); w.c.y = Y(obj->vertex, p2) - Y(obj->vertex, p0); w.c.z = Z(obj->vertex, p2) - Z(obj->vertex, p0); gr_vertex_normal (&normal, &v, &w); for (j=0; j<4; j++) { surf->elements[i+j].n[0] = normal.c.x; surf->elements[i+j].n[1] = normal.c.y; surf->elements[i+j].n[2] = normal.c.z; } } if (surf->smooth) accumulate_normal (obj, surf); } static void calc_normal_quad_strip (GrObject *obj, GrSurface *surf) { int i, j; GrVertex v, w, normal; int p0, p1, p2; if (surf->num_elements <= 2) return; for (i=3; inum_elements; i+=2) { p0 = surf->elements[i-3].ref; p1 = surf->elements[i-2].ref; p2 = surf->elements[i-1].ref; v.c.x = X(obj->vertex, p1) - X(obj->vertex, p0); v.c.y = Y(obj->vertex, p1) - Y(obj->vertex, p0); v.c.z = Z(obj->vertex, p1) - Z(obj->vertex, p0); w.c.x = X(obj->vertex, p2) - X(obj->vertex, p0); w.c.y = Y(obj->vertex, p2) - Y(obj->vertex, p0); w.c.z = Z(obj->vertex, p2) - Z(obj->vertex, p0); gr_vertex_normal (&normal, &v, &w); for (j=0; j<2; j++) { surf->elements[i-j].n[0] = normal.c.x; surf->elements[i-j].n[1] = normal.c.y; surf->elements[i-j].n[2] = normal.c.z; } } for (i=0; i<2; i++) { surf->elements[i].n[0] = surf->elements[2].n[0]; surf->elements[i].n[1] = surf->elements[2].n[1]; surf->elements[i].n[2] = surf->elements[2].n[2]; } if (surf->smooth) accumulate_normal (obj, surf); } static void calc_normal_polygon (GrObject *obj, GrSurface *surf) { int i; GrVertex v, w, normal; int p0, p1, p2; if (surf->num_elements <= 2) return; p0 = surf->elements[0].ref; p1 = surf->elements[1].ref; p2 = surf->elements[2].ref; v.c.x = X(obj->vertex, p1) - X(obj->vertex, p0); v.c.y = Y(obj->vertex, p1) - Y(obj->vertex, p0); v.c.z = Z(obj->vertex, p1) - Z(obj->vertex, p0); w.c.x = X(obj->vertex, p2) - X(obj->vertex, p0); w.c.y = Y(obj->vertex, p2) - Y(obj->vertex, p0); w.c.z = Z(obj->vertex, p2) - Z(obj->vertex, p0); gr_vertex_normal (&normal, &v, &w); for (i=0; inum_elements; i++) { surf->elements[i].n[0] = normal.c.x; surf->elements[i].n[1] = normal.c.y; surf->elements[i].n[2] = normal.c.z; } if (surf->smooth) accumulate_normal (obj, surf); } static GrSurface * read_surface (GrObject *obj, char *data, int *index) { int i; int type; GrSurface *surf; surf = gr_new (GrSurface, 1); if (!surf) return NULL; memset (surf, 0, sizeof(*surf)); next_word (data, index); sscanf (data + *index, "%d %d %d", &type, &surf->flag, &surf->num_elements); next_line (data, index); if (surf->flag & SURF_SHADED) surf->smooth = 1; if (surf->flag & SURF_TWOSIDE) surf->twoside = 1; surf->elements = gr_new (GrSElement, surf->num_elements); for (i=0; inum_elements; i++) { sscanf (data + *index, "%d %d %f %f", &surf->elements[i].mat, &surf->elements[i].ref, &surf->elements[i].s, &surf->elements[i].t); next_line (data, index); } switch (type) { case SURF_POINTS: surf->type = GL_POINTS; break; case SURF_LINES: surf->type = GL_LINES; break; case SURF_LINE_STRIP: surf->type = GL_LINE_STRIP; break; case SURF_TRIANGLES: surf->type = GL_TRIANGLES; calc_normal_triangles (obj, surf); break; case SURF_TRIANGLE_STRIP: surf->type = GL_TRIANGLE_STRIP; calc_normal_triangle_strip (obj, surf); break; case SURF_TRIANGLE_FAN: surf->type = GL_TRIANGLE_FAN; calc_normal_triangle_fan (obj, surf); break; case SURF_QUADS: surf->type = GL_QUADS; calc_normal_quads (obj, surf); break; case SURF_QUAD_STRIP: surf->type = GL_QUAD_STRIP; calc_normal_quad_strip (obj, surf); break; case SURF_POLYGON: surf->type = GL_POLYGON; calc_normal_polygon (obj, surf); break; default: surf->type = GL_NONE; } return surf; } static void gr_object_free (GrObject *obj) { int i; GR_BEGIN_FUNCTION (); if (obj) { if (obj->name) free (obj->name); if (obj->texture_name) free (obj->texture_name); if (obj->texture) gr_DECREF (obj->texture); if (obj->vertex) free (obj->vertex); if (obj->normal) free (obj->normal); if (obj->surfs) free (obj->surfs); if (obj->kids) { for (i=0; inum_kids; i++) { gr_DECREF (obj->kids[i]); } free (obj->kids); } free (obj); } } static GrObject * read_object (GrScene *scene, char *data, int *index) { GrObject *obj, *tmp; GrSurface *surf; int token; int j; float m[12]; float length; int flag; obj = gr_new0 (GrObject, 1); if (!obj) return NULL; gr_FREE_FUNC (obj, gr_object_free); obj->draw_option = GR_OBJECT_ALL; next_word (data, index); sscanf (data + *index, "%d", &obj->flag); next_word (data, index); obj->name = get_name (data, index); next_line (data, index); while (data[*index] != '\0') { token = find_token (data, index); switch (token) { case TOKEN_TEX: next_word (data, index); sscanf (data + *index, "%d", &flag); if (flag & 1) { obj->clamp_or_repeat_s = GL_REPEAT; } else { obj->clamp_or_repeat_s = GL_CLAMP; } if (flag & 2) { obj->clamp_or_repeat_t = GL_REPEAT; } else { obj->clamp_or_repeat_t = GL_CLAMP; } next_word (data, index); obj->texture_name = get_name (data, index); next_line (data, index); break; case TOKEN_ROT: next_word (data, index); sscanf (data + *index, "%f %f %f %f %f %f %f %f %f", &m[0], &m[4], &m[7], &m[1], &m[5], &m[8], &m[2], &m[6], &m[9]); next_line (data, index); break; case TOKEN_LOC: next_word (data, index); sscanf (data + *index, "%f %f %f", &obj->pos.c.x, &obj->pos.c.y, &obj->pos.c.z); next_line (data, index); break; case TOKEN_URL: next_word (data, index); obj->url = get_name (data, index); next_line (data, index); break; case TOKEN_NUMVERT: next_word (data, index); sscanf (data + *index, "%d", &(obj->num_verts)); next_line (data, index); if (obj->num_verts > 0) { obj->vertex = gr_new (GLfloat, obj->num_verts * 3); obj->normal = gr_new (GLfloat, obj->num_verts * 3); memset (obj->normal, 0, sizeof (GLfloat) * obj->num_verts * 3); for (j=0; jnum_verts; j++) { sscanf (data + *index, "%f %f %f", &X(obj->vertex, j), &Y(obj->vertex, j), &Z(obj->vertex, j)); next_line (data, index); } } break; case TOKEN_NUMSURF: next_word (data, index); sscanf (data + *index, "%d", &(obj->num_surfs)); next_line (data, index); if (obj->num_surfs > 0) { obj->surfs = gr_new (GrSurface *, obj->num_surfs); for (j=0; jnum_surfs; j++) { token = find_token (data, index); if (token != TOKEN_SURF) goto ERROR; surf = read_surface (obj, data, index); if (surf) obj->surfs[j] = surf; else goto ERROR; } } break; case TOKEN_KIDS: next_word (data, index); sscanf (data + *index, "%d", &(obj->num_kids)); next_line (data, index); if (obj->num_kids > 0) { obj->kids = gr_new (GrObject *, obj->num_kids); for (j=0; jnum_kids; j++) { token = find_token (data, index); if (token != TOKEN_OBJ) goto ERROR; tmp = read_object (scene, data, index); if (tmp) { obj->kids[j] = tmp; tmp->parent = obj; } } } obj->scene = scene; gr_INCREF(scene); for (j=0; jnum_verts; j++) { length = sqrt (X(obj->normal, j) * X(obj->normal, j) + Y(obj->normal, j) * Y(obj->normal, j) + Z(obj->normal, j) * Z(obj->normal, j)); if (length > 0) { X(obj->normal, j) /= length; Y(obj->normal, j) /= length; Z(obj->normal, j) /= length; } } return obj; default: goto ERROR; } } ERROR: if (obj) { gr_object_free (obj); } return NULL; } static GrObject * gr_object_get_nth_object (GrObject *obj, int *count) { int i; GrObject *res; if (*count == 0) return obj; (*count) --; for (i=0; inum_kids; i++) { res = gr_object_get_nth_object (obj->kids[i], count); if (res) return res; } return NULL; } static GrObject* gr_scene_get_nth_object (GrScene *scene, int num) { GrObject *obj; int count = num; int i; for (i=0; inum_objs; i++) { obj = gr_object_get_nth_object (scene->objs[i], &count); if (obj) return obj; } return NULL; } static GrSegment * read_segment (GrScene *scene, char *data, int *index) { GrSegment *seg; GrSubObject *subobj; GrSubSurface *subsurf; int token; int i, j; int num; seg = gr_new0 (GrSegment, 1); if (!seg) return NULL; next_word (data, index); seg->name = get_name (data, index); next_line (data, index); token = find_token (data, index); if (token != TOKEN_NUMOBJ) goto ERROR; next_word (data, index); sscanf (data + *index, "%d", &seg->num_objs); seg->objs = gr_new0 (GrSubObject *, seg->num_objs); if (!seg->objs) goto ERROR; next_word (data, index); for (i=0; inum_objs; i++) { token = find_token (data, index); if (token != TOKEN_OBJ) goto ERROR; subobj = seg->objs[i] = gr_new0 (GrSubObject, 1); if (!subobj) goto ERROR; next_word (data, index); sscanf (data + *index, "%d", &num); subobj->obj = gr_scene_get_nth_object (scene, num); if (!subobj->obj) goto ERROR; next_word (data, index); token = find_token (data, index); if (token != TOKEN_NUMSURF) goto ERROR; next_word (data, index); sscanf (data + *index, "%d", &subobj->num_surfs); subobj->surfs = gr_new0 (GrSubSurface *, subobj->num_surfs); if (!subobj->surfs) goto ERROR; next_word (data, index); for (j=0; jnum_surfs; j++) { token = find_token (data, index); if (token != TOKEN_SURF) goto ERROR; subsurf = subobj->surfs[j] = gr_new0 (GrSubSurface, 1); if (!subsurf) goto ERROR; next_word (data, index); sscanf (data + *index, "%d", &num); if (num < 0 || num >= subobj->obj->num_surfs) goto ERROR; subsurf->surf = subobj->obj->surfs[num]; next_word (data, index); sscanf (data + *index, "%d", &subsurf->start); if (subsurf->start < 0 || subsurf->start >= subsurf->surf->num_elements) goto ERROR; next_word (data, index); sscanf (data + *index, "%d", &subsurf->num); num = subsurf->start + subsurf->num; if (num < 0 || num >= subsurf->surf->num_elements) goto ERROR; next_word (data, index); } } return seg; ERROR: if (seg) { if (seg->objs) { for (i=0; inum_objs; i++) { if (seg->objs[i]) { if (seg->objs[i]->surfs) { for (j=0; jobjs[i]->num_surfs; j++) { free (seg->objs[i]->surfs[j]); } free (seg->objs[i]->surfs); } free (seg->objs[i]); } } free (seg->objs); } free (seg); } return NULL; } static void gr_object_calc_relative_position (GrObject *obj, GrVertex *abs, GrVertex **dst) { int i; assert (abs && dst); gr_vertex_add_a (abs, &obj->pos); if (*dst) { gr_vertex_sub (*dst, abs, *dst); } obj->next_pos = *abs; *dst = &obj->next_pos; for (i=0; inum_kids; i++) { gr_object_calc_relative_position (obj->kids[i], abs, dst); } gr_vertex_sub_a (abs, &obj->pos); } static void gr_object_calc_boundary (GrObject *obj) { int i; GrVertex min, max; GrObject *kid; for (i=0; inum_kids; i++) { gr_object_calc_boundary (obj->kids[i]); } if (obj->num_verts > 0) { min.c.x = max.c.x = X(obj->vertex, 0); min.c.y = max.c.y = Y(obj->vertex, 0); min.c.z = max.c.z = Z(obj->vertex, 0); for (i=1; inum_verts; i++) { if (min.c.x > X(obj->vertex, i)) { min.c.x = X(obj->vertex, i); } if (min.c.y > Y(obj->vertex, i)) { min.c.y = Y(obj->vertex, i); } if (min.c.z > Z(obj->vertex, i)) { min.c.z = Z(obj->vertex, i); } if (max.c.x < X(obj->vertex, i)) { max.c.x = X(obj->vertex, i); } if (max.c.y < Y(obj->vertex, i)) { max.c.y = Y(obj->vertex, i); } if (max.c.z < Z(obj->vertex, i)) { max.c.z = Z(obj->vertex, i); } } i = 0; } else if (obj->kids > 0) { min = obj->kids[0]->min; max = obj->kids[0]->max; i = 1; } else { return; } for (; inum_kids; i++) { kid = obj->kids[i]; if (min.c.x > kid->min.c.x) { min.c.x = kid->min.c.x; } if (min.c.y > kid->min.c.y) { min.c.y = kid->min.c.y; } if (min.c.z > kid->min.c.z) { min.c.z = kid->min.c.z; } if (max.c.x < kid->max.c.x) { max.c.x = kid->max.c.x; } if (max.c.y < kid->max.c.y) { max.c.y = kid->max.c.y; } if (max.c.z < kid->max.c.z) { max.c.z = kid->max.c.z; } } obj->min = min; obj->max = max; obj->center.c.x = (min.c.x + max.c.x) / 2; obj->center.c.y = (min.c.y + max.c.y) / 2; obj->center.c.z = (min.c.z + max.c.z) / 2; gr_vertex_sub_a (&max, &obj->center); obj->radius = gr_vertex_length (&max); } static void gr_scene_calc_boundary (GrScene *scene) { int i; GrVertex min, max; GrObject *obj; for (i=0; inum_objs; i++) { gr_object_calc_boundary (scene->objs[i]); } if (scene->num_objs <= 0) return; max = scene->objs[0]->max; min = scene->objs[0]->min; for (i=1; inum_objs; i++) { obj = scene->objs[i]; if (min.c.x > obj->min.c.x) { min.c.x = obj->min.c.x; } if (min.c.y > obj->min.c.y) { min.c.y = obj->min.c.y; } if (min.c.z > obj->min.c.z) { min.c.z = obj->min.c.z; } if (max.c.x < obj->max.c.x) { max.c.x = obj->max.c.x; } if (max.c.y < obj->max.c.y) { max.c.y = obj->max.c.y; } if (max.c.z < obj->max.c.z) { max.c.z = obj->max.c.z; } } scene->max = max; scene->min = min; scene->center.c.x = (min.c.x + max.c.x) / 2; scene->center.c.y = (min.c.y + max.c.y) / 2; scene->center.c.z = (min.c.z + max.c.z) / 2; gr_vertex_sub_a (&max, &scene->center); scene->radius = gr_vertex_length (&max); } GrScene * gr_scene_new_from_file (FILE *file) { char *buf; size_t len; size_t pos; GrScene *scene; pos = ftell (file); fseek (file, 0, SEEK_END); len = ftell (file) - pos; fseek (file, pos, SEEK_SET); buf = malloc (len+1); if (!buf) return NULL; fread (buf, 1, len, file); buf[len] = '\0'; scene = gr_scene_new_from_data (buf); free (buf); return scene; } static void gr_scene_free (GrScene *scene) { int i; GR_BEGIN_FUNCTION (); for (i=0; inum_objs; i++) { //scene->objs[i]->scene = NULL; gr_DECREF (scene->objs[i]); } free (scene->mats); free (scene->objs); free (scene); } GrScene * gr_scene_new_from_data (char *data) { int i = 0, j; GrScene *scene = NULL; GrMaterial *mat; GrObject *obj; GrSegment *seg; int token; GrVertex pos; GrVertex *dst; gr_return_val_if_fail (data != NULL, NULL); if (strncmp (data, "GRACER SCENE/1.0\n", 17)) goto ERROR; scene = gr_new0 (GrScene, 1); if (!scene) goto ERROR; gr_FREE_FUNC (scene, gr_scene_free); scene->draw_option = GR_OBJECT_DEFAULT; next_line (data, &i); while (data[i] != '\0') { token = find_token (data, &i); switch (token) { case TOKEN_NUMMAT: next_word (data, &i); sscanf (data + i, "%d", &scene->num_mats); scene->mats = gr_new (GrMaterial, scene->num_mats); next_line (data, &i); for (j=0; jnum_mats; j++) { token = find_token (data, &i); if (token != TOKEN_MAT) goto ERROR; mat = scene->mats + j; sscanf (data + i, "%d " "rgba %f %f %f %f " "amb %f %f %f %f " "dfs %f %f %f %f " "ems %f %f %f %f " "spc %f %f %f %f " "shi %d", &mat->flag, mat->rgba+0, mat->rgba+1, mat->rgba+2, mat->rgba+3, mat->amb+0, mat->amb+1, mat->amb+2, mat->amb+3, mat->dfs+0, mat->dfs+1, mat->dfs+2, mat->dfs+3, mat->ems+0, mat->ems+1, mat->ems+2, mat->ems+3, mat->spc+0, mat->spc+1, mat->spc+2, mat->spc+3, &mat->shi); next_line (data, &i); } break; case TOKEN_NUMOBJ: next_word (data, &i); sscanf (data + i, "%d", &scene->num_objs); scene->objs = gr_new (GrObject *, scene->num_objs); next_line (data, &i); for (j=0; jnum_objs; j++) { token = find_token (data, &i); if (token != TOKEN_OBJ) goto ERROR; obj = read_object (scene, data, &i); if (!obj) goto ERROR; scene->objs[j] = obj; } break; case TOKEN_NUMSEG: next_word (data, &i); sscanf (data+i, "%d", &scene->num_segs); scene->segs = gr_new (GrSegment *, scene->num_segs); next_line (data, &i); for (j=0; jnum_segs; j++) { token = find_token (data, &i); if (token != TOKEN_OBJ) goto ERROR; seg = read_segment (scene, data, &i); if (!seg) goto ERROR; scene->segs[j] = seg; } break; case TOKEN_EOF: goto FINISH; default: goto ERROR; } } FINISH: dst = NULL; pos = *gr_vertex_origin; for (i=0; inum_objs; i++) { gr_object_calc_relative_position (scene->objs[i], &pos, &dst); } gr_scene_calc_boundary (scene); return scene; ERROR: if (scene) { if (scene->mats) free (scene->mats); if (scene->objs) { for (i=0; inum_objs; i++) gr_DECREF (scene->objs[i]); free (scene->objs); } free (scene); } return NULL; } void gr_surface_free (GrSurface *surf) { GR_BEGIN_FUNCTION (); if (surf) { if (surf->elements) free (surf->elements); free (surf); } } static GrObject * find_named_object (GrObject *obj, char *name, GrObject **from) { int i; GrObject *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; } GrObject * gr_scene_find_object (GrScene *scene, char *name, GrObject *from) { int i; GrObject *obj; GrObject *_from = from; GR_BEGIN_FUNCTION (); 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 gr_scene_draw (GrScene *scene, GrObject *obj, GrObjectDrawOption option, int recursive) { int i; GrObject *o; int need_translate; GR_BEGIN_FUNCTION (); gr_return_if_fail (scene != NULL); option &= scene->draw_option; if (obj) { for (o=obj; o!=NULL; o=o->parent) { need_translate = (o->pos.c.x != 0.0 || o->pos.c.y != 0.0 || o->pos.c.z != 0.0); if (need_translate) { GL_CHECK(glTranslatef (o->pos.c.x, o->pos.c.y, o->pos.c.z)); } } gr_object_draw (obj, option, recursive); } else { if (scene->num_objs > 0) { need_translate = (scene->objs[0]->pos.c.x != 0.0 || scene->objs[0]->pos.c.y != 0.0 || scene->objs[0]->pos.c.z != 0.0); if (need_translate) { GL_CHECK(glTranslatef (scene->objs[0]->pos.c.x, scene->objs[0]->pos.c.y, scene->objs[0]->pos.c.z)); } } for (i=0; inum_objs; i++) { gr_object_draw (scene->objs[i], option, recursive); } } } void gr_object_draw (GrObject *obj, GrObjectDrawOption option, int recursive) { int i, j; GrSurface *surf; GrMaterial *matp; int mat; int use_texture; int use_normal; int use_smooth; int use_material; int use_color; GR_BEGIN_FUNCTION (); gr_return_if_fail (obj != NULL); option &= obj->draw_option; if (option && GR_OBJECT_DRAW) { if (obj->num_surfs > 0) { use_texture = obj->texture && (option & GR_OBJECT_TEXTURE); use_normal = option & GR_OBJECT_NORMAL; use_material = (option & GR_OBJECT_MATERIAL) != 0; use_color = (option & GR_OBJECT_COLOR) != 0; if (use_material) { GL_CHECK(glEnable (GL_COLOR_MATERIAL)); } GL_CHECK(glVertexPointer (3, GL_FLOAT, 0, obj->vertex)); if (use_normal) GL_CHECK(glNormalPointer (GL_FLOAT, 0, obj->normal)); GL_CHECK(glEnableClientState (GL_VERTEX_ARRAY)); GL_CHECK(glDisableClientState (GL_COLOR_ARRAY)); GL_CHECK(glDisableClientState (GL_EDGE_FLAG_ARRAY)); if (use_texture) { glEnable (GL_TEXTURE_2D); gr_texture_bind (obj->texture); } else { glDisable (GL_TEXTURE_2D); } mat = -1; for (i=0; inum_surfs; i++) { surf = obj->surfs[i]; use_smooth = use_normal && surf->smooth && (option & GR_OBJECT_SMOOTH); if (use_normal && use_smooth) { GL_CHECK(glShadeModel (GL_SMOOTH)); GL_CHECK(glEnableClientState (GL_NORMAL_ARRAY)); } else { GL_CHECK(glShadeModel (GL_FLAT)); GL_CHECK(glDisableClientState (GL_NORMAL_ARRAY)); } if (surf->twoside) { GL_CHECK(glDisable (GL_CULL_FACE)); //GL_CHECK(glLightModeli (GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE)); } glBegin (surf->type); for (j=0; jnum_elements; j++) { matp = &obj->scene->mats[surf->elements[j].mat]; if (use_material) { if (mat != surf->elements[j].mat) { mat = surf->elements[j].mat; glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT, matp->amb); glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, matp->dfs); glMaterialfv (GL_FRONT_AND_BACK, GL_EMISSION, matp->ems); glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, matp->spc); glMateriali (GL_FRONT_AND_BACK, GL_SHININESS, matp->shi); } if (use_color) { glColor4fv (matp->rgba); } } if (use_texture) glTexCoord2f (surf->elements[j].s, surf->elements[j].t); if (!use_smooth) { glNormal3fv (surf->elements[j].n); } glArrayElement (surf->elements[j].ref); } GL_CHECK(glEnd ()); if (surf->twoside) { GL_CHECK(glEnable (GL_CULL_FACE)); //GL_CHECK(glLightModeli (GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE)); } } } } if (recursive) { GL_CHECK(glTranslatef (obj->next_pos.c.x, obj->next_pos.c.y, obj->next_pos.c.z)); for (i=0; inum_kids; i++) { gr_object_draw (obj->kids[i], option, 1); } } } void gr_scene_scale (GrScene *scene, float x, float y, float z) { int i; GrVertex tmp; GR_BEGIN_FUNCTION (); gr_return_if_fail (scene != NULL); scene->center.c.x *= x; scene->center.c.y *= y; scene->center.c.z *= z; scene->min.c.x *= x; scene->min.c.y *= y; scene->min.c.z *= z; scene->max.c.x *= x; scene->max.c.y *= y; scene->max.c.z *= z; gr_vertex_sub (&tmp, &scene->max, &scene->center); scene->radius = gr_vertex_length (&tmp); for (i=0; inum_objs; i++) { gr_object_scale (scene->objs[i], x, y, z, 1); scene->objs[i]->pos.c.x *= x; scene->objs[i]->pos.c.y *= y; scene->objs[i]->pos.c.z *= z; } } void gr_object_scale (GrObject *obj, float x, float y, float z, int recursive) { int i; GrVertex tmp; GR_BEGIN_FUNCTION (); gr_return_if_fail (obj != NULL); for (i=0; inum_verts; i++) { X(obj->vertex, i) *= x; Y(obj->vertex, i) *= y; Z(obj->vertex, i) *= z; } obj->next_pos.c.x *= x; obj->next_pos.c.y *= y; obj->next_pos.c.z *= z; obj->center.c.x *= x; obj->center.c.y *= y; obj->center.c.z *= z; obj->min.c.x *= x; obj->min.c.y *= y; obj->min.c.z *= z; obj->max.c.x *= x; obj->max.c.y *= y; obj->max.c.z *= z; gr_vertex_sub (&tmp, &obj->max, &obj->center); obj->radius = gr_vertex_length (&tmp); if (recursive) { for (i=0; inum_kids; i++) { gr_object_scale (obj->kids[i], x, y, z, 1); obj->kids[i]->pos.c.x *= x; obj->kids[i]->pos.c.y *= y; obj->kids[i]->pos.c.z *= z; } } } static void gr_object_setup_gl (GrObject *obj, GrObjectDrawOption option) { int i; option &= obj->draw_option; if (obj->texture) { gr_texture_setup_gl (obj->texture, option); } for (i=0; inum_kids; i++) { gr_object_setup_gl (obj->kids[i], option); } } void gr_scene_setup_gl (GrScene *scene, GrObjectDrawOption option) { int i; option &= scene->draw_option; gr_return_if_fail (scene != NULL); for (i=0; inum_objs; i++) { gr_object_setup_gl (scene->objs[i], option); } } static void gr_object_release_gl (GrObject *obj) { int i; if (obj->texture) { gr_texture_release_gl (obj->texture); } for (i=0; inum_kids; i++) { gr_object_release_gl (obj->kids[i]); } } void gr_scene_release_gl (GrScene *scene) { int i; GR_BEGIN_FUNCTION (); gr_return_if_fail (scene != NULL); for (i=0; inum_objs; i++) { gr_object_release_gl (scene->objs[i]); } } void gr_object_absolute_coord (GrObject *obj, float x, float y, float z, int recursive) { int i; gr_return_if_fail (obj != NULL); x += obj->pos.c.x; y += obj->pos.c.y; z += obj->pos.c.z; for (i=0; inum_verts; i++) { X(obj->vertex, i) += x; Y(obj->vertex, i) += y; Z(obj->vertex, i) += z; } obj->pos = *gr_vertex_origin; if (recursive) { for (i=0; inum_kids; i++) { gr_object_absolute_coord (obj->kids[i], x, y, z, 1); } } } static void gr_object_write_file (GrObject *obj, FILE *file) { GrSurface *surf; int flag = 0; int type; int i, j; fprintf (file, "obj %d \"%s\"\n", obj->flag, obj->name? obj->name:""); if (obj->texture_name) { flag = 0; if (obj->clamp_or_repeat_s == GL_REPEAT) flag |= 1; if (obj->clamp_or_repeat_t == GL_REPEAT) flag |= 2; fprintf (file, "texture %d \"%s\"\n", flag, obj->texture_name); } fprintf (file, "loc %f %f %f\n", obj->pos.c.x, obj->pos.c.y, obj->pos.c.z); if (obj->url) { fprintf (file, "url \"%s\"\n", obj->url); } fprintf (file, "numvert %d\n", obj->num_verts); for (i=0; inum_verts; i++) { fprintf (file, "%f %f %f\n", X(obj->vertex, i), Y(obj->vertex, i), Z(obj->vertex, i)); } fprintf (file, "numsurf %d\n", obj->num_surfs); for (i=0; inum_surfs; i++) { surf = obj->surfs[i]; switch (surf->type) { case GL_POINTS: type = SURF_POINTS; break; case GL_LINES: type = SURF_LINES; break; case GL_LINE_STRIP: type = SURF_LINE_STRIP; break; case GL_TRIANGLES: type = SURF_TRIANGLES; break; case GL_TRIANGLE_STRIP: type = SURF_TRIANGLE_STRIP; break; case GL_TRIANGLE_FAN: type = SURF_TRIANGLE_FAN; break; case GL_QUADS: type = SURF_QUADS; break; case GL_QUAD_STRIP: type = SURF_QUAD_STRIP; break; case GL_POLYGON: type = SURF_POLYGON; break; default: type = -1; } flag = 0; if (surf->smooth) flag |= SURF_SHADED; if (surf->twoside) flag |= SURF_TWOSIDE; fprintf (file, "surf %d %d %d\n", type, flag, surf->num_elements); for (j=0; jnum_elements; j++) { fprintf (file, "%d %d %f %f\n", surf->elements[j].mat, surf->elements[j].ref, surf->elements[j].s, surf->elements[j].t); } } fprintf (file, "numkid %d\n", obj->num_kids); for (i=0; inum_kids; i++) { gr_object_write_file (obj->kids[i], file); } } void gr_scene_write_file (GrScene *scene, FILE *file) { int i; fprintf (file, "GRACER SCENE/1.0\n"); fprintf (file, "nummat %d\n", scene->num_mats); for (i=0; inum_mats; i++) { fprintf (file, "mat %d " "rgba %f %f %f %f " "amb %f %f %f %f " "dfs %f %f %f %f " "ems %f %f %f %f " "spc %f %f %f %f " "shi %d\n", scene->mats[i].flag, scene->mats[i].rgba[0], scene->mats[i].rgba[1], scene->mats[i].rgba[2], scene->mats[i].rgba[3], scene->mats[i].amb[0], scene->mats[i].amb[1], scene->mats[i].amb[2], scene->mats[i].amb[3], scene->mats[i].dfs[0], scene->mats[i].dfs[1], scene->mats[i].dfs[2], scene->mats[i].dfs[3], scene->mats[i].ems[0], scene->mats[i].ems[1], scene->mats[i].ems[2], scene->mats[i].ems[3], scene->mats[i].spc[0], scene->mats[i].spc[1], scene->mats[i].spc[2], scene->mats[i].spc[3], scene->mats[i].shi); } fprintf (file, "numobj %d\n", scene->num_objs); for (i=0; inum_objs; i++) { gr_object_write_file (scene->objs[i], file); } }