#include "database.h" #include "matrix.h" #include "seads.h" #ifdef DEBUG #define DBG(s) s #else #define DBG(s) #endif /* global external variables */ extern int new_face; extern FILE *outFile; /* used to keep a track of object ids */ static unsigned long objNum = 0; static void calcPolyInfo(PolyNode *, TuppleArray *); /* Takes a vertex and adds it to the vertex array va and updates the array's * count of vertices (total) * * Params vt the vertex tupple to add * va pointer to the vertex array to add to * * Globals none * * Returns total of vertices in the vertex array */ int addTupple(vt, va) Tupple vt; TuppleArray *va; { (va->total)++; if (va->total == 1) va->v = (Tupple *)malloc(sizeof(Tupple)*2); else va->v = (Tupple *)realloc(va->v,sizeof(Tupple)*(va->total+1)); if (va->v == NULL) { fprintf(stderr, "Error couldn't malloc/realloc tupple array\n"); exit(1); } va->v[(va->total)] = vt; DBG(printf("vertex %d:\t%f %f %f\n",va->total, va->v[va->total].i, va->v[va->total].j, va->v[va->total].k);) return(va->total); } /* * creates the faces of an object by adding vertices (and optionally normals) * to the current PolyNode, or starts a new PolyNode if the global variable * new_face is True. * * Params obj pointer to the object to add to * vtxI which vertex to add (index into array obj->va.v) * texI which texture vertex to add (unused, haven't passed * texture array) * normI which normal to add (index into array obj->vn.v) * * Global new_face has a new face been encountered in the input file? * * Returns nothing */ void obj_face(obj, vtxI, normI) ObjNode *obj; int vtxI, normI; { static PolyNode *pp; if (new_face) { new_face = False; /* increment polygon count and add room for new face */ (obj->polys.total)++; obj->polys.p = (PolyNode *)realloc(obj->polys.p, sizeof(PolyNode) * obj->polys.total); if (obj->polys.p == NULL) { fprintf(stderr, "Error couldn't realloc PolyNode array\n"); exit(1); } /* initialize empty PolyNode */ pp = &(obj->polys.p[obj->polys.total-1]); pp->vertices = NULL; pp->vertNorms = NULL; pp->numVertices = 0; } /* handle negative referencing */ if (vtxI < 0) vtxI += obj->va.total; if (normI < 0) normI += obj->vn.total; /* assert: pp points to the current polygon to add to. * Add vertex index to vertex index array */ (pp->numVertices)++; pp->vertices = (int *)realloc(pp->vertices,sizeof(int)*pp->numVertices); if (pp->vertices == NULL) { fprintf(stderr, "Error couldn't malloc/realloc vertex index array\n"); exit(1); } pp->vertices[pp->numVertices-1] = vtxI; /* add vertex normal (if there is one) * Assuming that all or no vertices of an object have vertex normals * so the number of vertices = number of vertex normals when the normals * exist. */ if (normI != 0) { pp->vertNorms=(int *)realloc(pp->vertNorms,sizeof(int)*pp->numVertices); if (pp->vertNorms == NULL) { fprintf(stderr, "Error couldn't malloc/realloc vertex normal index \ array\n"); exit(1); } pp->vertNorms[pp->numVertices-1] = normI; } } /* * Takes a polygon/face and calculates the normal, and the value d where the * plane the polygon lies in is represented by: ax + by + cz + d = 0 * Calculates the bounding box info (min and max x values etc) * Also calculates i1 and i2 used by the intersection algorithm. * * Params pp pointer to the face * va pointer to the vertex array * * Globals none * * Returns nothing */ static void calcPolyInfo(pp, va) PolyNode *pp; TuppleArray *va; { Real a, b, c, d, e, f, g, h, i; int *v = pp->vertices; if (va->total < 3 || pp->numVertices < 3) { fprintf(stderr, "Error: vertex array has less than 3 vertices\n"); exit(2); } /* calc normal of the plane the polygon lies in by taking the * cross product of 2 vectors (edges) assuming they aren't * colinear */ a = va->v[v[1]].i - va->v[v[0]].i; b = va->v[v[1]].j - va->v[v[0]].j; c = va->v[v[1]].k - va->v[v[0]].k; d = va->v[v[2]].i - va->v[v[1]].i; e = va->v[v[2]].j - va->v[v[1]].j; f = va->v[v[2]].k - va->v[v[1]].k; pp->normal.i = b * f - c * e; pp->normal.j = c * d - a * f; pp->normal.k = a * e - d * b; if (!normalize(&(pp->normal))) { fprintf(stderr, "Error magnitude of polygon's normal vector is 0\n"); exit(2); } g = pp->normal.i; h = pp->normal.j; i = pp->normal.k; /* calculate value d where plane represented by the equation: * Normal . Point on plane + d = 0 */ pp->d = -(g * va->v[v[0]].i + h * va->v[v[0]].j + i * va->v[v[0]].k); /* calc i1 and i2 for use in ray polygon intersection (see Graphics Gems * vol 1) */ a = (Real)fabs((Real)g); b = (Real)fabs((Real)h); c = (Real)fabs((Real)i); if (a < b) { pp->i1 = 0; if (b < c) pp->i2 = 1; else pp->i2 = 2; } else { pp->i1 = 1; if (a < c) pp->i2 = 0; else pp->i2 = 2; } } /* * Creates an empty object node and adds it to the head of the list of object * nodes pointed to by obj. Each object is assigned a * unique id number which is used with the collision interest matrix. * * Params obj pointer to the list of objects * * Globals objNum count of object ids (next object will be assigned this * id). * * Returns pointer to created object node which is at the head of the list */ ObjNode *addEmptyObjNode(obj) ObjNode *obj; { ObjNode *op = (ObjNode *)malloc(sizeof(ObjNode)); if (op == NULL) { fprintf(stderr, "Error can't malloc object Node\n"); exit(1); } op->id = objNum++; op->va.total = 0; op->vn.total = 0; op->va.v = NULL; op->vn.v = NULL; op->polys.total = 0; op->polys.p = NULL; op->min.i = 0.0; op->min.j = 0.0; op->min.k = 0.0; op->max = op->min; op->gmin = op->min; op->gmax = op->min; op->seads = NULL; op->loc2glob = NULL; op->glob2loc = NULL; op->ctrans = NULL; op->info = NULL; op->next = obj; op->valid = True; return op; } /* * outputs an object in showpoly format * * Params obj object * * Globals outFile output file * * Returns nothing */ void showPolyOutputObj(obj) ObjNode *obj; { int i, j, nv, *v; Tupple *va = NULL; if (obj == NULL) return; if (!(obj->valid)) return; va = (Tupple *)malloc(sizeof(Tupple)*(obj->va.total + 1)); if (va == NULL) { fprintf(stderr, "Error: couldn't malloc Tupple array\n"); exit(1); } for (i=1; i <= obj->va.total; i++) applyTransform(obj->loc2glob, obj->va.v[i], &va[i]); for (j=0; j < obj->polys.total; j++) { fprintf(outFile, "Poly "); v = obj->polys.p[j].vertices; for (i=0, nv=obj->polys.p[j].numVertices; i < nv; i++) fprintf(outFile, "<%f %f %f> ", va[v[i]].i, va[v[i]].j, va[v[i]].k); fprintf(outFile, "\n"); } free(va); } /* * outputs the given object in wave front object format. * * Params obj object to output * vaOffset vertex offset * vnOffset vertex normal offset * * Globals outFile output file * * Returns updated vertex and vertex normal offsets after outputing this * object's vertices and vertex normals */ void waveFrontOutputObj(obj, vaOffset, vnOffset) ObjNode *obj; int *vaOffset, *vnOffset; { int i, j, limit; Tupple *va, *vn; PolyNode *pp; /* output vertices for this object */ va = (Tupple *)malloc(sizeof(Tupple)*(obj->va.total + 1)); if (va == NULL) { fprintf(stderr, "Error: couldn't malloc Tupple array\n"); exit(1); } /* put vertices into global frame before printing out */ for (i=1, limit=obj->va.total; i <= limit; i++) applyTransform(obj->loc2glob, obj->va.v[i], &va[i]); for (i=1; i<=limit; i++) fprintf(outFile, "v %f %f %f\n", va[i].i, va[i].j, va[i].k); fprintf(outFile, "# vertices %d\n", limit); /* put vertex normals into global frame (if they exist) */ vn = (Tupple *)malloc(sizeof(Tupple)*(obj->vn.total + 1)); if (vn == NULL) { fprintf(stderr, "Error: couldn't malloc Tupple array\n"); exit(1); } for (i=1, limit=obj->vn.total; i <= limit; i++) transformNormal(obj->glob2loc, obj->vn.v[i], &vn[i]); /* output vertex normals (if they exist) */ for (i=1; i<=limit; i++) fprintf(outFile, "vn %f %f %f\n", vn[i].i, vn[i].j, vn[i].k); fprintf(outFile, "# vertex normals %d\n", limit); /* output polygons */ if (obj->vn.total == 0) { /* no vertex normals */ for (j=0; j < obj->polys.total; j++) { fprintf(outFile, "f"); pp = &(obj->polys.p[j]); for (i=0, limit=pp->numVertices; i< limit; i++) fprintf(outFile, " %d", *vaOffset+pp->vertices[i]); fprintf(outFile, "\n"); } } else { /* have vertex normals */ for (j=0; j < obj->polys.total; j++) { fprintf(outFile, "f"); pp = &(obj->polys.p[j]); for (i=0, limit=pp->numVertices; i< limit; i++) fprintf(outFile, " %d//%d", *vaOffset+pp->vertices[i], *vnOffset+pp->vertNorms[i]); fprintf(outFile, "\n"); } } fprintf(outFile, "\n"); *vaOffset += obj->va.total; *vnOffset += obj->vn.total; free(va); free(vn); } /* * calls calcPolyInfo to set up the polygons belonging to this object, then * calls createSEADS to set up the SEADS * * Params obj pointer to object * * Globals none * * returns nothing */ void calcObjInfo(obj) ObjNode *obj; { int i; if (obj == NULL) return; /* calc info for all the polys */ for (i=0; i < obj->polys.total; i++) calcPolyInfo(&(obj->polys.p[i]), &(obj->va)); createSEADS(obj); } /* * Given a vector (a tupple of numbers), normalize it. That is, make the * magnitude = 1 * * Params v pointer to the vector * * Globals none * * Returns False if the magnitude was 0 * True otherwise */ Boolean normalize(v) Tupple *v; { Real mag = sqrt(SQR(v->i) + SQR(v->j) + SQR(v->k)); if (mag == 0.0) return False; v->i /= mag; v->j /= mag; v->k /= mag; return True; } /* * Translates all the points of an object, so that all the points lie in the * positive octant (ie, i,j,k >= 0). * * Params obj pointer to object * trans pointer to Tupple to return the translation * * Globals none * * Returns the translation used to move the object in the Tupple trans */ void zeroObject(obj, trans) ObjNode *obj; Tupple *trans; { register int i, limit; Tupple min, *verts; verts = obj->va.v; min = verts[1]; /* find the minimum values */ for (i=2, limit=obj->va.total; i<=limit; i++) { if (verts[i].i < min.i) min.i = verts[i].i; if (verts[i].j < min.j) min.j = verts[i].j; if (verts[i].k < min.k) min.k = verts[i].k; } /* use the minimum values to translate the object so that the minimum * i, j, k values (across all sets of points) are 0 */ for (i=1; i<=limit; i++) { verts[i].i -= min.i; verts[i].j -= min.j; verts[i].k -= min.k; } trans->i = 0.0 - min.i; trans->j = 0.0 - min.j; trans->k = 0.0 - min.k; } /* * removes the head of a list of object nodes (which corresponds to the latest * addition). Decrements the global object count, but otherwise doesn't alter * anything else (ie. doesn't delete the vertex arrays etc, in case this * object is sharing data with another. It is up to the user to do that). * * Params objs list of objects * * Globals objNum count of objects * * Returns the object list with the head removed */ ObjNode *removeFirstObjNode(objs) ObjNode *objs; { ObjNode *ot = objs; objs = objs->next; objNum--; free (ot); return objs; }