// map.c #include "bsp5.h" int nummapbrushes; mbrush_t mapbrushes[max_map_brushes]; int num_entities; entity_t entities[max_map_entities]; int nummiptex; char miptex[max_map_texinfo][16]; //============================================================================ /* =============== findmiptex =============== */ int findmiptex (char *name) { int i; for (i=0 ; imiptex][0] == '*' || !q_strncasecmp (miptex[t->miptex], "sky",3) ) t->flags |= tex_special; tex = texinfo; for (i=0 ; imiptex != tex->miptex) continue; if (t->flags != tex->flags) continue; for (j=0 ; j<8 ; j++) if (t->vecs[0][j] != tex->vecs[0][j]) break; if (j != 8) continue; return i; } // allocate a new texture if (numtexinfo == max_map_texinfo) error ("numtexinfo == max_map_texinfo"); texinfo[i] = *t; numtexinfo++; return i; } //============================================================================ #define maxtoken 128 char token[maxtoken]; qboolean unget; char *script_p; int scriptline; void starttokenparsing (char *data) { scriptline = 1; script_p = data; unget = false; } qboolean gettoken (qboolean crossline) { char *token_p; if (unget) // is a token allready waiting? return true; // // skip space // skipspace: while (*script_p <= 32) { if (!*script_p) { if (!crossline) error ("line %i is incomplete",scriptline); return false; } if (*script_p++ == '\n') { if (!crossline) error ("line %i is incomplete",scriptline); scriptline++; } } if (script_p[0] == '/' && script_p[1] == '/') // comment field { if (!crossline) error ("line %i is incomplete\n",scriptline); while (*script_p++ != '\n') if (!*script_p) { if (!crossline) error ("line %i is incomplete",scriptline); return false; } goto skipspace; } // // copy token // token_p = token; if (*script_p == '"') { script_p++; while ( *script_p != '"' ) { if (!*script_p) error ("eof inside quoted token"); *token_p++ = *script_p++; if (token_p > &token[maxtoken-1]) error ("token too large on line %i",scriptline); } script_p++; } else while ( *script_p > 32 ) { *token_p++ = *script_p++; if (token_p > &token[maxtoken-1]) error ("token too large on line %i",scriptline); } *token_p = 0; return true; } void ungettoken () { unget = true; } //============================================================================ entity_t *mapent; /* ================= parseepair ================= */ void parseepair (void) { epair_t *e; e = malloc (sizeof(epair_t)); memset (e, 0, sizeof(epair_t)); e->next = mapent->epairs; mapent->epairs = e; if (strlen(token) >= max_key-1) error ("parseepar: token too long"); e->key = copystring(token); gettoken (false); if (strlen(token) >= max_value-1) error ("parseepar: token too long"); e->value = copystring(token); } //============================================================================ /* ================== textureaxisfromplane ================== */ vec3_t baseaxis[18] = { {0,0,1}, {1,0,0}, {0,-1,0}, // floor {0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling {1,0,0}, {0,1,0}, {0,0,-1}, // west wall {-1,0,0}, {0,1,0}, {0,0,-1}, // east wall {0,1,0}, {1,0,0}, {0,0,-1}, // south wall {0,-1,0}, {1,0,0}, {0,0,-1} // north wall }; void textureaxisfromplane(plane_t *pln, vec3_t xv, vec3_t yv) { int bestaxis; float dot,best; int i; best = 0; bestaxis = 0; for (i=0 ; i<6 ; i++) { dot = dotproduct (pln->normal, baseaxis[i*3]); if (dot > best) { best = dot; bestaxis = i; } } vectorcopy (baseaxis[bestaxis*3+1], xv); vectorcopy (baseaxis[bestaxis*3+2], yv); } //============================================================================= /* ================= parsebrush ================= */ void parsebrush (void) { mbrush_t *b; mface_t *f, *f2; vec3_t planepts[3]; vec3_t t1, t2, t3; int i,j; texinfo_t tx; vec_t d; float shift[2], rotate, scale[2]; b = &mapbrushes[nummapbrushes]; nummapbrushes++; b->next = mapent->brushes; mapent->brushes = b; do { if (!gettoken (true)) break; if (!strcmp (token, "}") ) break; // read the three point plane definition for (i=0 ; i<3 ; i++) { if (i != 0) gettoken (true); if (strcmp (token, "(") ) error ("parsing brush"); for (j=0 ; j<3 ; j++) { gettoken (false); planepts[i][j] = atoi(token); } gettoken (false); if (strcmp (token, ")") ) error ("parsing brush"); } // read the texturedef memset (&tx, 0, sizeof(tx)); gettoken (false); tx.miptex = findmiptex (token); gettoken (false); shift[0] = atoi(token); gettoken (false); shift[1] = atoi(token); gettoken (false); rotate = atoi(token); gettoken (false); scale[0] = atof(token); gettoken (false); scale[1] = atof(token); // if the three points are all on a previous plane, it is a // duplicate plane for (f2 = b->faces ; f2 ; f2=f2->next) { for (i=0 ; i<3 ; i++) { d = dotproduct(planepts[i],f2->plane.normal) - f2->plane.dist; if (d < -on_epsilon || d > on_epsilon) break; } if (i==3) break; } if (f2) { printf ("warning: brush with duplicate plane\n"); continue; } f = malloc(sizeof(mface_t)); f->next = b->faces; b->faces = f; // convert to a vector / dist plane for (j=0 ; j<3 ; j++) { t1[j] = planepts[0][j] - planepts[1][j]; t2[j] = planepts[2][j] - planepts[1][j]; t3[j] = planepts[1][j]; } crossproduct(t1,t2, f->plane.normal); if (vectorcompare (f->plane.normal, vec3_origin)) { printf ("warning: brush plane with no normal\n"); b->faces = f->next; free (f); break; } vectornormalize (f->plane.normal); f->plane.dist = dotproduct (t3, f->plane.normal); // // fake proper texture vectors from quakeed style // { vec3_t vecs[2]; int sv, tv; float ang, sinv, cosv; float ns, nt; textureaxisfromplane(&f->plane, vecs[0], vecs[1]); if (!scale[0]) scale[0] = 1; if (!scale[1]) scale[1] = 1; // rotate axis if (rotate == 0) { sinv = 0 ; cosv = 1; } else if (rotate == 90) { sinv = 1 ; cosv = 0; } else if (rotate == 180) { sinv = 0 ; cosv = -1; } else if (rotate == 270) { sinv = -1 ; cosv = 0; } else { ang = rotate / 180 * q_pi; sinv = sin(ang); cosv = cos(ang); } if (vecs[0][0]) sv = 0; else if (vecs[0][1]) sv = 1; else sv = 2; if (vecs[1][0]) tv = 0; else if (vecs[1][1]) tv = 1; else tv = 2; for (i=0 ; i<2 ; i++) { ns = cosv * vecs[i][sv] - sinv * vecs[i][tv]; nt = sinv * vecs[i][sv] + cosv * vecs[i][tv]; vecs[i][sv] = ns; vecs[i][tv] = nt; } for (i=0 ; i<2 ; i++) for (j=0 ; j<3 ; j++) tx.vecs[i][j] = vecs[i][j] / scale[i]; tx.vecs[0][3] = shift[0]; tx.vecs[1][3] = shift[1]; } // unique the texinfo f->texinfo = findtexinfo (&tx); } while (1); } /* ================ parseentity ================ */ qboolean parseentity (void) { if (!gettoken (true)) return false; if (strcmp (token, "{") ) error ("parseentity: { not found"); if (num_entities == max_map_entities) error ("num_entities == max_map_entities"); mapent = &entities[num_entities]; num_entities++; do { if (!gettoken (true)) error ("parseentity: eof without closing brace"); if (!strcmp (token, "}") ) break; if (!strcmp (token, "{") ) parsebrush (); else parseepair (); } while (1); getvectorforkey (mapent, "origin", mapent->origin); return true; } /* ================ loadmapfile ================ */ void loadmapfile (char *filename) { char *buf; loadfile (filename, (void **)&buf); starttokenparsing (buf); num_entities = 0; while (parseentity ()) { } free (buf); qprintf ("--- loadmapfile ---\n"); qprintf ("%s\n", filename); qprintf ("%5i brushes\n", nummapbrushes); qprintf ("%5i entities\n", num_entities); qprintf ("%5i miptex\n", nummiptex); qprintf ("%5i texinfo\n", numtexinfo); } void printentity (entity_t *ent) { epair_t *ep; for (ep=ent->epairs ; ep ; ep=ep->next) printf ("%20s : %s\n", ep->key, ep->value); } char *valueforkey (entity_t *ent, char *key) { epair_t *ep; for (ep=ent->epairs ; ep ; ep=ep->next) if (!strcmp (ep->key, key) ) return ep->value; return ""; } void setkeyvalue (entity_t *ent, char *key, char *value) { epair_t *ep; for (ep=ent->epairs ; ep ; ep=ep->next) if (!strcmp (ep->key, key) ) { free (ep->value); ep->value = copystring(value); return; } ep = malloc (sizeof(*ep)); ep->next = ent->epairs; ent->epairs = ep; ep->key = copystring(key); ep->value = copystring(value); } float floatforkey (entity_t *ent, char *key) { char *k; k = valueforkey (ent, key); return atof(k); } void getvectorforkey (entity_t *ent, char *key, vec3_t vec) { char *k; double v1, v2, v3; k = valueforkey (ent, key); v1 = v2 = v3 = 0; // scanf into doubles, then assign, so it is vec_t size independent sscanf (k, "%lf %lf %lf", &v1, &v2, &v3); vec[0] = v1; vec[1] = v2; vec[2] = v3; } void writeentitiestostring (void) { char *buf, *end; epair_t *ep; char line[128]; int i; buf = dentdata; end = buf; *end = 0; for (i=0 ; inext) { sprintf (line, "\"%s\" \"%s\"\n", ep->key, ep->value); strcat (end, line); end += strlen(line); } strcat (end,"}\n"); end += 2; if (end > buf + max_map_entstring) error ("entity text too long"); } entdatasize = end - buf + 1; }