1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18 See file, 'COPYING', for details.
19 */
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 #ifdef HAVE_STRING_H
25 # include <string.h>
26 #endif
27 #ifdef HAVE_STRINGS_H
28 # include <strings.h>
29 #endif
30 #include <stdlib.h>
31 #include <errno.h>
32 #include <ctype.h>
33
34 #include "QF/dstring.h"
35 #include "QF/hash.h"
36 #include "QF/quakefs.h"
37 #include "QF/script.h"
38 #include "QF/sys.h"
39 #include "QF/va.h"
40
41 #include "compat.h"
42
43 #include "map.h"
44
45 /** \addtogroup qfbsp_map
46 */
47 //@{
48
49 int nummapbrushfaces;
50 int nummapbrushes;
51
52 #define ENTITIES_CHUNK 16
53 int num_entities;
54 int max_entities;
55 entity_t *entities;
56
57 #define MIPTEXNAME_CHUNK 16
58 int nummiptexnames;
59 int maxmiptexnames;
60 const char **miptexnames;
61 hashtab_t *miptex_hash;
62
63 int numdetailbrushes;
64
65 script_t *map_script;
66
67 static void __attribute__ ((format (printf, 1, 2), noreturn))
map_error(const char * fmt,...)68 map_error (const char *fmt, ...)
69 {
70 va_list args;
71
72 va_start (args, fmt);
73 fprintf (stderr, "%s:%d: ", map_script->file, map_script->line);
74 vfprintf (stderr, fmt, args);
75 fprintf (stderr, "\n");
76 va_end (args);
77 exit (1);
78 }
79
80 static const char *
miptex_getkey(const void * key,void * unused)81 miptex_getkey (const void *key, void *unused)
82 {
83 intptr_t index = (intptr_t) key;
84 return miptexnames[index - 1];
85 }
86
87 int
FindMiptex(const char * name)88 FindMiptex (const char *name)
89 {
90 intptr_t index;
91 char mpname[MIPTEXNAME];
92
93 strncpy (mpname, name, MIPTEXNAME - 1);
94 mpname[MIPTEXNAME - 1] = 0;
95 if (strcmp (name, "hint") == 0)
96 return TEX_HINT;
97 if (strcmp (name, "skip") == 0)
98 return TEX_SKIP;
99 if (!miptex_hash)
100 miptex_hash = Hash_NewTable (1023, miptex_getkey, 0, 0);
101 if (miptexnames) {
102 index = (intptr_t) Hash_Find (miptex_hash, mpname);
103 if (index)
104 return index - 1;
105 }
106 if (nummiptexnames == maxmiptexnames) {
107 maxmiptexnames += MIPTEXNAME_CHUNK;
108 miptexnames = realloc (miptexnames,
109 maxmiptexnames * sizeof (const char *));
110 }
111 index = nummiptexnames++;
112 miptexnames[index] = strdup (mpname);
113 Hash_Add (miptex_hash, (void *) (index + 1));
114 return index;
115 }
116
117 /*
118 FindTexinfo
119
120 Returns a global texinfo number
121 */
122 static int
FindTexinfo(texinfo_t * t)123 FindTexinfo (texinfo_t *t)
124 {
125 int i, j;
126 texinfo_t *tex;
127
128 if (t->miptex < 0)
129 return t->miptex; // it's HINT or SKIP
130
131 // set the special flag
132 if (miptexnames[t->miptex][0] == '*'
133 || !strncasecmp (miptexnames[t->miptex], "sky", 3))
134 t->flags |= TEX_SPECIAL;
135
136 tex = bsp->texinfo;
137 for (i = 0; i < bsp->numtexinfo; i++, tex++) {
138 if (t->miptex != tex->miptex)
139 continue;
140 if (t->flags != tex->flags)
141 continue;
142
143 for (j = 0; j < 8; j++)
144 if (t->vecs[j / 4][j % 4] != tex->vecs[j / 4][j % 4])
145 break;
146 if (j != 8)
147 continue;
148
149 return i;
150 }
151
152 // allocate a new texture
153 BSP_AddTexinfo (bsp, t);
154
155 return i;
156 }
157
158 entity_t *mapent;
159
160 static void
ParseEpair(void)161 ParseEpair (void)
162 {
163 epair_t *e;
164
165 e = malloc (sizeof (epair_t));
166 memset (e, 0, sizeof (epair_t));
167 e->next = mapent->epairs;
168 mapent->epairs = e;
169
170 e->key = strdup (map_script->token->str);
171 Script_GetToken (map_script, false);
172 e->value = strdup (map_script->token->str);
173 }
174
175 vec3_t baseaxis[18] = {
176 {0, 0, 1}, {1, 0, 0}, {0, -1, 0}, // floor
177 {0, 0, -1}, {1, 0, 0}, {0, -1, 0}, // ceiling
178 {1, 0, 0}, {0, 1, 0}, {0, 0, -1}, // west wall
179 {-1, 0, 0}, {0, 1, 0}, {0, 0, -1}, // east wall
180 {0, 1, 0}, {1, 0, 0}, {0, 0, -1}, // south wall
181 {0, -1, 0}, {1, 0, 0}, {0, 0, -1} // north wall
182 };
183
184 static void
TextureAxisFromPlane(plane_t * pln,vec3_t xv,vec3_t yv)185 TextureAxisFromPlane (plane_t *pln, vec3_t xv, vec3_t yv)
186 {
187 float dot, best;
188 int bestaxis, i;
189
190 best = 0;
191 bestaxis = 0;
192
193 for (i = 0; i < 6; i++) {
194 dot = DotProduct (pln->normal, baseaxis[i * 3]);
195 if (dot > best) {
196 best = dot;
197 bestaxis = i;
198 }
199 }
200
201 VectorCopy (baseaxis[bestaxis * 3 + 1], xv);
202 VectorCopy (baseaxis[bestaxis * 3 + 2], yv);
203 }
204
205 static vec3_t *
ParseVerts(int * n_verts)206 ParseVerts (int *n_verts)
207 {
208 vec3_t *verts;
209 int i;
210 const char *token;
211
212 token = Script_Token (map_script);
213 if (token[0] != ':')
214 map_error ("parsing brush");
215 // It's normally ":count", but somebody might have done ": count"
216 if (!token[1]) {
217 Script_GetToken (map_script, false);
218 token = Script_Token (map_script);
219 } else {
220 token++;
221 }
222 *n_verts = atoi (token);
223 verts = malloc (sizeof (vec3_t) * *n_verts);
224
225 for (i = 0; i < *n_verts; i++) {
226 Script_GetToken (map_script, true);
227 verts[i][0] = atof (map_script->token->str);
228 Script_GetToken (map_script, true);
229 verts[i][1] = atof (map_script->token->str);
230 Script_GetToken (map_script, true);
231 verts[i][2] = atof (map_script->token->str);
232 }
233
234 return verts;
235 }
236
237 static void
ParseBrush(void)238 ParseBrush (void)
239 {
240 float rotate, scale[2];
241 float ang, sinv, cosv, ns, nt;
242 int sv, tv;
243 int i, j, n_verts = 0, hltexdef;
244 mbrush_t *b;
245 mface_t *f, *f2;
246 plane_t plane;
247 texinfo_t tx;
248 vec3_t planepts[3];
249 vec3_t t1, t2, *verts = 0;
250 vec_t d, vecs[2][4];
251
252 nummapbrushes++;
253 b = calloc (1, sizeof (mbrush_t));
254 b->next = mapent->brushes;
255 mapent->brushes = b;
256
257 Script_GetToken (map_script, true);
258 if (strcmp (map_script->token->str, "(") != 0) {
259 verts = ParseVerts (&n_verts);
260 } else {
261 Script_UngetToken (map_script);
262 }
263
264 do {
265 if (!Script_GetToken (map_script, true))
266 break;
267 if (!strcmp (map_script->token->str, "}"))
268 break;
269
270 if (verts) {
271 int n_v, v;
272 n_v = atoi (map_script->token->str);
273 Script_GetToken (map_script, false);
274 for (i = 0; i < n_v; i++) {
275 Script_GetToken (map_script, false);
276 v = atof (map_script->token->str);
277 if (i < 3)
278 VectorCopy (verts[v], planepts[i]);
279 }
280 Script_GetToken (map_script, false);
281 } else {
282 // read the three point plane definition
283 for (i = 0; i < 3; i++) {
284 if (i != 0)
285 Script_GetToken (map_script, true);
286 if (strcmp (map_script->token->str, "("))
287 map_error ("parsing brush");
288
289 for (j = 0; j < 3; j++) {
290 Script_GetToken (map_script, false);
291 planepts[i][j] = atof (map_script->token->str);
292 }
293
294 Script_GetToken (map_script, false);
295 if (strcmp (map_script->token->str, ")"))
296 map_error ("parsing brush");
297 }
298 }
299
300 // convert points to a plane
301 VectorSubtract (planepts[0], planepts[1], t1);
302 VectorSubtract (planepts[2], planepts[1], t2);
303 CrossProduct(t1, t2, plane.normal);
304 VectorNormalize (plane.normal);
305 plane.dist = DotProduct(planepts[1], plane.normal);
306
307 // read the texturedef
308 memset (&tx, 0, sizeof (tx));
309 Script_GetToken (map_script, false);
310 tx.miptex = FindMiptex (map_script->token->str);
311 Script_GetToken (map_script, false);
312 if ((hltexdef = !strcmp (map_script->token->str, "["))) {
313 // S vector
314 Script_GetToken (map_script, false);
315 vecs[0][0] = atof (map_script->token->str);
316 Script_GetToken (map_script, false);
317 vecs[0][1] = atof (map_script->token->str);
318 Script_GetToken (map_script, false);
319 vecs[0][2] = atof (map_script->token->str);
320 Script_GetToken (map_script, false);
321 vecs[0][3] = atof (map_script->token->str);
322 Script_GetToken (map_script, false);
323 if (strcmp (map_script->token->str, "]"))
324 map_error ("missing ]");
325 Script_GetToken (map_script, false);
326 if (strcmp (map_script->token->str, "["))
327 map_error ("missing [");
328 // T vector
329 Script_GetToken (map_script, false);
330 vecs[1][0] = atof (map_script->token->str);
331 Script_GetToken (map_script, false);
332 vecs[1][1] = atof (map_script->token->str);
333 Script_GetToken (map_script, false);
334 vecs[1][2] = atof (map_script->token->str);
335 Script_GetToken (map_script, false);
336 vecs[1][3] = atof (map_script->token->str);
337 Script_GetToken (map_script, false);
338 if (strcmp (map_script->token->str, "]"))
339 map_error ("missing ]");
340 } else {
341 vecs[0][3] = atof (map_script->token->str);
342 Script_GetToken (map_script, false);
343 vecs[1][3] = atof (map_script->token->str);
344 }
345
346 Script_GetToken (map_script, false);
347 rotate = atof (map_script->token->str);
348 Script_GetToken (map_script, false);
349 scale[0] = atof (map_script->token->str);
350 Script_GetToken (map_script, false);
351 scale[1] = atof (map_script->token->str);
352
353 while (Script_TokenAvailable (map_script, false)) {
354 Script_GetToken (map_script, false);
355 if (!strcmp (map_script->token->str, "detail"))
356 b->detail = 1;
357 else
358 map_error ("parse error");
359 }
360
361 // if the three points are all on a previous plane, it is a duplicate
362 // plane
363 for (f2 = b->faces; f2; f2 = f2->next) {
364 for (i = 0; i < 3; i++) {
365 d = DotProduct (planepts[i], f2->plane.normal)
366 - f2->plane.dist;
367 if (d < -ON_EPSILON || d > ON_EPSILON)
368 break;
369 }
370 if (i == 3)
371 break;
372 }
373 if (f2) {
374 printf ("WARNING: brush with duplicate plane\n");
375 continue;
376 }
377
378 if (DotProduct (plane.normal, plane.normal) < 0.1) {
379 printf ("WARNING: brush plane with no normal on line %d\n",
380 map_script->line);
381 continue;
382 }
383
384 if (!hltexdef) {
385 // fake proper texture vectors from QuakeEd style
386 TextureAxisFromPlane (&plane, vecs[0], vecs[1]);
387 }
388
389 if (!scale[0])
390 scale[0] = 1;
391 if (!scale[1])
392 scale[1] = 1;
393
394 // rotate axis
395 if (rotate == 0) {
396 sinv = 0;
397 cosv = 1;
398 } else if (rotate == 90) {
399 sinv = 1;
400 cosv = 0;
401 } else if (rotate == 180) {
402 sinv = 0;
403 cosv = -1;
404 } else if (rotate == 270) {
405 sinv = -1;
406 cosv = 0;
407 } else {
408 ang = rotate / 180 * M_PI;
409 sinv = sin (ang);
410 cosv = cos (ang);
411 }
412
413 if (vecs[0][0])
414 sv = 0;
415 else if (vecs[0][1])
416 sv = 1;
417 else
418 sv = 2;
419
420 if (vecs[1][0])
421 tv = 0;
422 else if (vecs[1][1])
423 tv = 1;
424 else
425 tv = 2;
426
427 for (i = 0; i < 2; i++) {
428 // rotate
429 ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
430 nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
431 vecs[i][sv] = ns;
432 vecs[i][tv] = nt;
433 // scale and store into texinfo
434 for (j = 0; j < 3; j++)
435 tx.vecs[i][j] = vecs[i][j] / scale[i];
436 tx.vecs[i][3] = vecs[i][3];
437 }
438
439 f = calloc (1, sizeof (mface_t));
440 f->next = b->faces;
441 b->faces = f;
442 f->plane = plane;
443 // unique the texinfo
444 f->texinfo = FindTexinfo (&tx);
445 nummapbrushfaces++;
446 } while (1);
447 if (verts)
448 free (verts);
449 }
450
451 static qboolean
ParseEntity(void)452 ParseEntity (void)
453 {
454 if (!Script_GetToken (map_script, true))
455 return false;
456
457 if (strcmp (map_script->token->str, "{"))
458 map_error ("ParseEntity: { not found");
459
460 if (num_entities == max_entities) {
461 max_entities += ENTITIES_CHUNK;
462 entities = realloc (entities, max_entities * sizeof (entity_t));
463 }
464
465 mapent = &entities[num_entities];
466 num_entities++;
467 memset (mapent, 0, sizeof (entity_t));
468 mapent->line = map_script->line;
469
470 do {
471 if (!Script_GetToken (map_script, true))
472 map_error ("ParseEntity: EOF without closing brace");
473 if (!strcmp (map_script->token->str, "}"))
474 break;
475 if (!strcmp (map_script->token->str, "{"))
476 ParseBrush ();
477 else
478 ParseEpair ();
479 } while (1);
480
481 if (!strcmp ("am_detail", ValueForKey (mapent, "classname"))) {
482 mbrush_t *b, *lb;
483 // set detail flag
484 for (lb = b = mapent->brushes; b; lb = b, b = b->next) {
485 b->detail = 1;
486 numdetailbrushes++;
487 }
488 // add to worldspawn
489 lb->next = entities->brushes;
490 entities->brushes = mapent->brushes;
491 num_entities--;
492 memset (mapent, 0, sizeof (entity_t));
493 return true;
494 } else {
495 mbrush_t *b;
496 for (b = mapent->brushes; b; b = b->next)
497 if (b->detail)
498 numdetailbrushes++;
499 }
500
501 GetVectorForKey (mapent, "origin", mapent->origin);
502 return true;
503 }
504
505 void
LoadMapFile(const char * filename)506 LoadMapFile (const char *filename)
507 {
508 char *buf;
509 QFile *file;
510 int bytes;
511
512 file = Qopen (filename, "rt");
513 if (!file)
514 Sys_Error ("couldn't open %s. %s", filename, strerror(errno));
515 bytes = Qfilesize (file);
516 buf = malloc (bytes + 1);
517 bytes = Qread (file, buf, bytes);
518 buf[bytes] = 0;
519 Qclose (file);
520
521 map_script = Script_New ();
522 map_script->single = "";
523 Script_Start (map_script, filename, buf);
524
525 num_entities = 0;
526
527 if (Script_GetToken (map_script, 1)) {
528 if (strequal (map_script->token->str, "QuakeForge-map")) {
529 if (!Script_TokenAvailable (map_script, 1))
530 map_error ("Unexpected EOF reading %s\n", filename);
531 } else {
532 Script_UngetToken (map_script);
533 while (ParseEntity ())
534 ;
535 }
536 }
537
538 Script_Delete (map_script);
539 map_script = 0;
540 free (buf);
541
542 qprintf ("--- LoadMapFile ---\n");
543 qprintf ("%s\n", filename);
544 qprintf ("%5i faces\n", nummapbrushfaces);
545 qprintf ("%5i brushes (%i detail)\n", nummapbrushes, numdetailbrushes);
546 qprintf ("%5i entities\n", num_entities);
547 qprintf ("%5i textures\n", nummiptexnames);
548 qprintf ("%5i texinfo\n", bsp->numtexinfo);
549 }
550
551 void
PrintEntity(const entity_t * ent)552 PrintEntity (const entity_t *ent)
553 {
554 const epair_t *ep;
555
556 printf ("%20s : %d\n", "map source line", ent->line);
557 for (ep = ent->epairs; ep; ep = ep->next)
558 printf ("%20s : %s\n", ep->key, ep->value);
559 }
560
561
562 const char *
ValueForKey(const entity_t * ent,const char * key)563 ValueForKey (const entity_t *ent, const char *key)
564 {
565 const epair_t *ep;
566
567 for (ep = ent->epairs; ep; ep = ep->next)
568 if (!strcmp (ep->key, key))
569 return ep->value;
570 return "";
571 }
572
573 void
SetKeyValue(entity_t * ent,const char * key,const char * value)574 SetKeyValue (entity_t *ent, const char *key, const char *value)
575 {
576 epair_t *ep;
577
578 for (ep = ent->epairs; ep; ep = ep->next)
579 if (!strcmp (ep->key, key)) {
580 free (ep->value);
581 ep->value = strdup (value);
582 return;
583 }
584 ep = malloc (sizeof (*ep));
585 ep->next = ent->epairs;
586 ent->epairs = ep;
587 ep->key = strdup (key);
588 ep->value = strdup (value);
589 }
590
591 void
GetVectorForKey(const entity_t * ent,const char * key,vec3_t vec)592 GetVectorForKey (const entity_t *ent, const char *key, vec3_t vec)
593 {
594 const char *k;
595 double v1, v2, v3;
596
597 k = ValueForKey (ent, key);
598 v1 = v2 = v3 = 0;
599 // scanf into doubles, then assign, so it is vec_t size independent
600 sscanf (k, "%lf %lf %lf", &v1, &v2, &v3);
601 vec[0] = v1;
602 vec[1] = v2;
603 vec[2] = v3;
604 }
605
606 void
WriteEntitiesToString(void)607 WriteEntitiesToString (void)
608 {
609 dstring_t *buf;
610 const epair_t *ep;
611 int i;
612
613 buf = dstring_newstr ();
614
615 for (i = 0; i < num_entities; i++) {
616 ep = entities[i].epairs;
617 if (!ep)
618 continue; // ent got removed
619
620 dstring_appendstr (buf, "{\n");
621
622 for (ep = entities[i].epairs; ep; ep = ep->next) {
623 dstring_appendstr (buf, va ("\"%s\" \"%s\"\n",
624 ep->key, ep->value));
625 }
626 dstring_appendstr (buf, "}\n");
627 }
628 BSP_AddEntities (bsp, buf->str, buf->size);
629 }
630
631 //@}
632