Quake II RTX doxygen  1.0 dev
bsp.c
Go to the documentation of this file.
1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 Copyright (C) 2008 Andrey Nazarov
4 Copyright (C) 2019, NVIDIA CORPORATION. All rights reserved.
5 
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 
21 // bsp.c -- model loading
22 
23 #include "shared/shared.h"
24 #include "shared/list.h"
25 #include "common/cvar.h"
26 #include "common/cmd.h"
27 #include "common/common.h"
28 #include "common/files.h"
29 #include "common/bsp.h"
30 #include "common/math.h"
31 #include "common/utils.h"
32 #include "common/mdfour.h"
33 #include "system/hunk.h"
34 
35 extern mtexinfo_t nulltexinfo;
36 
37 static cvar_t *map_visibility_patch;
38 
39 /*
40 ===============================================================================
41 
42  LUMP LOADING
43 
44 ===============================================================================
45 */
46 
47 #define ALLOC(size) \
48  Hunk_Alloc(&bsp->hunk, size)
49 
50 #define LOAD(func) \
51  static qerror_t BSP_Load##func(bsp_t *bsp, void *base, size_t count)
52 
53 #define DEBUG(msg) \
54  Com_DPrintf("%s: %s\n", __func__, msg)
55 
56 LOAD(Visibility)
57 {
58  uint32_t numclusters, bitofs;
59  int i, j;
60 
61  if (!count) {
62  return Q_ERR_SUCCESS;
63  }
64 
65  if (count < 4) {
66  DEBUG("too small header");
67  return Q_ERR_TOO_FEW;
68  }
69 
70  bsp->numvisibility = count;
71  bsp->vis = ALLOC(count);
72  memcpy(bsp->vis, base, count);
73 
74  numclusters = LittleLong(bsp->vis->numclusters);
75  if (numclusters > MAX_MAP_LEAFS) {
76  DEBUG("bad numclusters");
77  return Q_ERR_TOO_MANY;
78  }
79 
80  if (numclusters > (count - 4) / 8) {
81  DEBUG("too small header");
82  return Q_ERR_TOO_FEW;
83  }
84 
85  bsp->vis->numclusters = numclusters;
86  bsp->visrowsize = (numclusters + 7) >> 3;
87 
88  for (i = 0; i < numclusters; i++) {
89  for (j = 0; j < 2; j++) {
90  bitofs = LittleLong(bsp->vis->bitofs[i][j]);
91  if (bitofs >= count) {
92  DEBUG("bad bitofs");
93  return Q_ERR_BAD_INDEX;
94  }
95  bsp->vis->bitofs[i][j] = bitofs;
96  }
97  }
98 
99  return Q_ERR_SUCCESS;
100 }
101 
102 LOAD(Texinfo)
103 {
104  dtexinfo_t *in;
105  mtexinfo_t *out;
106  int i;
107 #if USE_REF
108  int j, k;
109  int32_t next;
110  mtexinfo_t *step;
111 #endif
112 
113  bsp->numtexinfo = count;
114  bsp->texinfo = ALLOC(sizeof(*out) * count);
115 
116  in = base;
117  out = bsp->texinfo;
118  for (i = 0; i < count; i++, in++, out++) {
119  memcpy(out->c.name, in->texture, sizeof(out->c.name));
120  out->c.name[sizeof(out->c.name) - 1] = 0;
121  memcpy(out->name, in->texture, sizeof(out->name));
122  out->name[sizeof(out->name) - 1] = 0;
123  out->c.flags = LittleLong(in->flags);
124  out->c.value = LittleLong(in->value);
125 
126 #if USE_REF
127  out->radiance = in->value;
128  for (j = 0; j < 2; j++) {
129  for (k = 0; k < 3; k++) {
130  out->axis[j][k] = LittleFloat(in->vecs[j][k]);
131  }
132  out->offset[j] = LittleFloat(in->vecs[j][k]);
133  }
134 
135  next = (int32_t)LittleLong(in->nexttexinfo);
136  if (next > 0) {
137  if (next >= count) {
138  DEBUG("bad anim chain");
139  return Q_ERR_BAD_INDEX;
140  }
141  out->next = bsp->texinfo + next;
142  } else {
143  out->next = NULL;
144  }
145 #endif
146  }
147 
148 #if USE_REF
149  // count animation frames
150  out = bsp->texinfo;
151  for (i = 0; i < count; i++, out++) {
152  out->numframes = 1;
153  for (step = out->next; step && step != out; step = step->next) {
154  if (out->numframes == count) {
155  DEBUG("infinite anim chain");
156  return Q_ERR_INFINITE_LOOP;
157  }
158  out->numframes++;
159  }
160  }
161 #endif
162 
163  return Q_ERR_SUCCESS;
164 }
165 
166 LOAD(Planes)
167 {
168  dplane_t *in;
169  cplane_t *out;
170  int i, j;
171 
172  bsp->numplanes = count;
173  bsp->planes = ALLOC(sizeof(*out) * count);
174 
175  in = base;
176  out = bsp->planes;
177  for (i = 0; i < count; i++, in++, out++) {
178  for (j = 0; j < 3; j++) {
179  out->normal[j] = LittleFloat(in->normal[j]);
180  }
181  out->dist = LittleFloat(in->dist);
182  SetPlaneType(out);
183  SetPlaneSignbits(out);
184  }
185 
186  return Q_ERR_SUCCESS;
187 }
188 
189 LOAD(BrushSides)
190 {
191  dbrushside_t *in;
192  mbrushside_t *out;
193  int i;
194  uint16_t planenum, texinfo;
195 
196  bsp->numbrushsides = count;
197  bsp->brushsides = ALLOC(sizeof(*out) * count);
198 
199  in = base;
200  out = bsp->brushsides;
201  for (i = 0; i < count; i++, in++, out++) {
202  planenum = LittleShort(in->planenum);
203  if (planenum >= bsp->numplanes) {
204  DEBUG("bad planenum");
205  return Q_ERR_BAD_INDEX;
206  }
207  out->plane = bsp->planes + planenum;
208  texinfo = LittleShort(in->texinfo);
209  if (texinfo == (uint16_t)-1) {
210  out->texinfo = &nulltexinfo;
211  } else {
212  if (texinfo >= bsp->numtexinfo) {
213  DEBUG("bad texinfo");
214  return Q_ERR_BAD_INDEX;
215  }
216  out->texinfo = bsp->texinfo + texinfo;
217  }
218  }
219 
220  return Q_ERR_SUCCESS;
221 }
222 
223 LOAD(Brushes)
224 {
225  dbrush_t *in;
226  mbrush_t *out;
227  int i;
228  uint32_t firstside, numsides, lastside;
229 
230  bsp->numbrushes = count;
231  bsp->brushes = ALLOC(sizeof(*out) * count);
232 
233  in = base;
234  out = bsp->brushes;
235  for (i = 0; i < count; i++, out++, in++) {
236  firstside = LittleLong(in->firstside);
237  numsides = LittleLong(in->numsides);
238  lastside = firstside + numsides;
239  if (lastside < firstside || lastside > bsp->numbrushsides) {
240  DEBUG("bad brushsides");
241  return Q_ERR_BAD_INDEX;
242  }
243  out->firstbrushside = bsp->brushsides + firstside;
244  out->numsides = numsides;
245  out->contents = LittleLong(in->contents);
246  out->checkcount = 0;
247  }
248 
249  return Q_ERR_SUCCESS;
250 }
251 
252 LOAD(LeafBrushes)
253 {
254  uint16_t *in;
255  mbrush_t **out;
256  int i;
257  uint16_t brushnum;
258 
259  bsp->numleafbrushes = count;
260  bsp->leafbrushes = ALLOC(sizeof(*out) * count);
261 
262  in = base;
263  out = bsp->leafbrushes;
264  for (i = 0; i < count; i++, in++, out++) {
265  brushnum = LittleShort(*in);
266  if (brushnum >= bsp->numbrushes) {
267  DEBUG("bad brushnum");
268  return Q_ERR_BAD_INDEX;
269  }
270  *out = bsp->brushes + brushnum;
271  }
272 
273  return Q_ERR_SUCCESS;
274 }
275 
276 
277 #if USE_REF
278 LOAD(Lightmap)
279 {
280  if (!count) {
281  return Q_ERR_SUCCESS;
282  }
283 
284  bsp->numlightmapbytes = count;
285  bsp->lightmap = ALLOC(count);
286 
287  memcpy(bsp->lightmap, base, count);
288 
289  return Q_ERR_SUCCESS;
290 }
291 
292 LOAD(Vertices)
293 {
294  dvertex_t *in;
295  mvertex_t *out;
296  int i, j;
297 
298  bsp->numvertices = count;
299  bsp->vertices = ALLOC(sizeof(*out) * count);
300 
301  in = base;
302  out = bsp->vertices;
303  for (i = 0; i < count; i++, out++, in++) {
304  for (j = 0; j < 3; j++) {
305  out->point[j] = LittleFloat(in->point[j]);
306  }
307  }
308 
309  return Q_ERR_SUCCESS;
310 }
311 
312 LOAD(Edges)
313 {
314  dedge_t *in;
315  medge_t *out;
316  int i, j;
317  uint16_t vertnum;
318 
319  bsp->numedges = count;
320  bsp->edges = ALLOC(sizeof(*out) * count);
321 
322  in = base;
323  out = bsp->edges;
324  for (i = 0; i < count; i++, out++, in++) {
325  for (j = 0; j < 2; j++) {
326  vertnum = LittleShort(in->v[j]);
327  if (vertnum >= bsp->numvertices) {
328  DEBUG("bad vertnum");
329  return Q_ERR_BAD_INDEX;
330  }
331  out->v[j] = bsp->vertices + vertnum;
332  }
333  }
334 
335  return Q_ERR_SUCCESS;
336 }
337 
338 LOAD(SurfEdges)
339 {
340  int *in;
341  msurfedge_t *out;
342  int i, vert;
343  int32_t index;
344 
345  bsp->numsurfedges = count;
346  bsp->surfedges = ALLOC(sizeof(*out) * count);
347 
348  in = base;
349  out = bsp->surfedges;
350  for (i = 0; i < count; i++, out++, in++) {
351  index = (int32_t)LittleLong(*in);
352 
353  vert = 0;
354  if (index < 0) {
355  index = -index;
356  vert = 1;
357  }
358 
359  if (index >= bsp->numedges) {
360  DEBUG("bad edgenum");
361  return Q_ERR_BAD_INDEX;
362  }
363 
364  out->edge = bsp->edges + index;
365  out->vert = vert;
366  }
367 
368  return Q_ERR_SUCCESS;
369 }
370 
371 LOAD(Faces)
372 {
373  dface_t *in;
374  mface_t *out;
375  int i, j;
376  uint32_t firstedge, numedges, lastedge;
377  uint16_t planenum, texinfo, side;
378  uint32_t lightofs;
379 
380  bsp->numfaces = count;
381  bsp->faces = ALLOC(sizeof(*out) * count);
382 
383  in = base;
384  out = bsp->faces;
385  for (i = 0; i < count; i++, in++, out++) {
386  firstedge = LittleLong(in->firstedge);
387  numedges = LittleShort(in->numedges);
388  lastedge = firstedge + numedges;
389  if (numedges < 3) {
390  DEBUG("bad surfedges");
391  return Q_ERR_TOO_FEW;
392  }
393  if (numedges > 4096) {
394  DEBUG("bad surfedges");
395  return Q_ERR_TOO_MANY;
396  }
397  if (lastedge < firstedge || lastedge > bsp->numsurfedges) {
398  DEBUG("bad surfedges");
399  return Q_ERR_BAD_INDEX;
400  }
401  out->firstsurfedge = bsp->surfedges + firstedge;
402  out->numsurfedges = numedges;
403 
404  planenum = LittleShort(in->planenum);
405  if (planenum >= bsp->numplanes) {
406  DEBUG("bad planenum");
407  return Q_ERR_BAD_INDEX;
408  }
409  out->plane = bsp->planes + planenum;
410 
411  texinfo = LittleShort(in->texinfo);
412  if (texinfo >= bsp->numtexinfo) {
413  DEBUG("bad texinfo");
414  return Q_ERR_BAD_INDEX;
415  }
416  out->texinfo = bsp->texinfo + texinfo;
417 
418  for (j = 0; j < MAX_LIGHTMAPS && in->styles[j] != 255; j++) {
419  out->styles[j] = in->styles[j];
420  }
421  out->numstyles = j;
422  for (; j < MAX_LIGHTMAPS; j++) {
423  out->styles[j] = 255;
424  }
425 
426  lightofs = LittleLong(in->lightofs);
427  if (lightofs == (uint32_t)-1 || bsp->numlightmapbytes == 0) {
428  out->lightmap = NULL;
429  } else {
430  if (lightofs >= bsp->numlightmapbytes) {
431  DEBUG("bad lightofs");
432  return Q_ERR_BAD_INDEX;
433  }
434  out->lightmap = bsp->lightmap + lightofs;
435  }
436 
437  side = LittleShort(in->side);
438  out->drawflags = side & DSURF_PLANEBACK;
439  }
440 
441  return Q_ERR_SUCCESS;
442 }
443 
444 LOAD(LeafFaces)
445 {
446  uint16_t *in;
447  mface_t **out;
448  int i;
449  uint16_t facenum;
450 
451  bsp->numleaffaces = count;
452  bsp->leaffaces = ALLOC(sizeof(*out) * count);
453 
454  in = base;
455  out = bsp->leaffaces;
456  for (i = 0; i < count; i++, in++, out++) {
457  facenum = LittleShort(*in);
458  if (facenum >= bsp->numfaces) {
459  DEBUG("bad facenum");
460  return Q_ERR_BAD_INDEX;
461  }
462  *out = bsp->faces + facenum;
463  }
464 
465  return Q_ERR_SUCCESS;
466 }
467 #endif
468 
469 LOAD(Leafs)
470 {
471  dleaf_t *in;
472  mleaf_t *out;
473  int i;
474  uint16_t cluster, area;
475  uint16_t firstleafbrush, numleafbrushes, lastleafbrush;
476 #if USE_REF
477  int j;
478  uint16_t firstleafface, numleaffaces, lastleafface;
479 #endif
480 
481  if (!count) {
482  DEBUG("map with no leafs");
483  return Q_ERR_TOO_FEW;
484  }
485 
486  bsp->numleafs = count;
487  bsp->leafs = ALLOC(sizeof(*out) * count);
488 
489  in = base;
490  out = bsp->leafs;
491  for (i = 0; i < count; i++, in++, out++) {
492  out->plane = NULL;
493  out->contents = LittleLong(in->contents);
494  cluster = LittleShort(in->cluster);
495  if (cluster == (uint16_t)-1) {
496  // solid leafs use special -1 cluster
497  out->cluster = -1;
498  } else if (bsp->vis == NULL) {
499  // map has no vis, use 0 as a default cluster
500  out->cluster = 0;
501  } else {
502  // validate cluster
503  if (cluster >= bsp->vis->numclusters) {
504  DEBUG("bad cluster");
505  return Q_ERR_BAD_INDEX;
506  }
507  out->cluster = cluster;
508  }
509 
510  area = LittleShort(in->area);
511  if (area >= bsp->numareas) {
512  DEBUG("bad area");
513  return Q_ERR_BAD_INDEX;
514  }
515  out->area = area;
516 
517  firstleafbrush = LittleShort(in->firstleafbrush);
518  numleafbrushes = LittleShort(in->numleafbrushes);
519  lastleafbrush = firstleafbrush + numleafbrushes;
520  if (lastleafbrush < firstleafbrush || lastleafbrush > bsp->numleafbrushes) {
521  DEBUG("bad leafbrushes");
522  return Q_ERR_BAD_INDEX;
523  }
524  out->firstleafbrush = bsp->leafbrushes + firstleafbrush;
525  out->numleafbrushes = numleafbrushes;
526 
527 #if USE_REF
528  firstleafface = LittleShort(in->firstleafface);
529  numleaffaces = LittleShort(in->numleaffaces);
530  lastleafface = firstleafface + numleaffaces;
531  if (lastleafface < firstleafface || lastleafface > bsp->numleaffaces) {
532  DEBUG("bad leaffaces");
533  return Q_ERR_BAD_INDEX;
534  }
535  out->firstleafface = bsp->leaffaces + firstleafface;
536  out->numleaffaces = numleaffaces;
537 
538  for (j = 0; j < 3; j++) {
539  out->mins[j] = (int16_t)LittleShort(in->mins[j]);
540  out->maxs[j] = (int16_t)LittleShort(in->maxs[j]);
541  }
542 
543  out->parent = NULL;
544  out->visframe = -1;
545 #endif
546  }
547 
548  if (bsp->leafs[0].contents != CONTENTS_SOLID) {
549  DEBUG("map leaf 0 is not CONTENTS_SOLID");
550  return Q_ERR_INVALID_FORMAT;
551  }
552 
553  return Q_ERR_SUCCESS;
554 }
555 
556 LOAD(Nodes)
557 {
558  dnode_t *in;
559  mnode_t *out;
560  int i, j;
561  uint32_t planenum, child;
562 #if USE_REF
563  uint16_t firstface, numfaces, lastface;
564 #endif
565 
566  if (!count) {
567  DEBUG("map with no nodes");
568  return Q_ERR_TOO_FEW;
569  }
570 
571  bsp->numnodes = count;
572  bsp->nodes = ALLOC(sizeof(*out) * count);
573 
574  in = base;
575  out = bsp->nodes;
576  for (i = 0; i < count; i++, out++, in++) {
577  planenum = LittleLong(in->planenum);
578  if (planenum >= bsp->numplanes) {
579  DEBUG("bad planenum");
580  return Q_ERR_BAD_INDEX;
581  }
582  out->plane = bsp->planes + planenum;
583 
584  for (j = 0; j < 2; j++) {
585  child = LittleLong(in->children[j]);
586  if (child & 0x80000000) {
587  child = ~child;
588  if (child >= bsp->numleafs) {
589  DEBUG("bad leafnum");
590  return Q_ERR_BAD_INDEX;
591  }
592  out->children[j] = (mnode_t *)(bsp->leafs + child);
593  } else {
594  if (child >= count) {
595  DEBUG("bad nodenum");
596  return Q_ERR_BAD_INDEX;
597  }
598  out->children[j] = bsp->nodes + child;
599  }
600  }
601 
602 #if USE_REF
603  firstface = LittleShort(in->firstface);
604  numfaces = LittleShort(in->numfaces);
605  lastface = firstface + numfaces;
606  if (lastface < firstface || lastface > bsp->numfaces) {
607  DEBUG("bad faces");
608  return Q_ERR_BAD_INDEX;
609  }
610  out->firstface = bsp->faces + firstface;
611  out->numfaces = numfaces;
612 
613  for (j = 0; j < 3; j++) {
614  out->mins[j] = (int16_t)LittleShort(in->mins[j]);
615  out->maxs[j] = (int16_t)LittleShort(in->maxs[j]);
616  }
617 
618  out->parent = NULL;
619  out->visframe = -1;
620 #endif
621  }
622 
623  return Q_ERR_SUCCESS;
624 }
625 
626 LOAD(Submodels)
627 {
628  dmodel_t *in;
629  mmodel_t *out;
630  int i, j;
631  uint32_t headnode;
632 #if USE_REF
633  uint32_t firstface, numfaces, lastface;
634 #endif
635 
636  if (!count) {
637  DEBUG("map with no models");
638  return Q_ERR_TOO_FEW;
639  }
640 
641  bsp->models = ALLOC(sizeof(*out) * count);
642  bsp->nummodels = count;
643 
644  in = base;
645  out = bsp->models;
646  for (i = 0; i < count; i++, in++, out++) {
647  for (j = 0; j < 3; j++) {
648  // spread the mins / maxs by a pixel
649  out->mins[j] = LittleFloat(in->mins[j]) - 1;
650  out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
651  out->origin[j] = LittleFloat(in->origin[j]);
652  }
653  headnode = LittleLong(in->headnode);
654  if (headnode & 0x80000000) {
655  // be careful, some models have no nodes, just a leaf
656  headnode = ~headnode;
657  if (headnode >= bsp->numleafs) {
658  DEBUG("bad headleaf");
659  return Q_ERR_BAD_INDEX;
660  }
661  out->headnode = (mnode_t *)(bsp->leafs + headnode);
662  } else {
663  if (headnode >= bsp->numnodes) {
664  DEBUG("bad headnode");
665  return Q_ERR_BAD_INDEX;
666  }
667  out->headnode = bsp->nodes + headnode;
668  }
669 #if USE_REF
670  if (i == 0) {
671  continue;
672  }
673  firstface = LittleLong(in->firstface);
674  numfaces = LittleLong(in->numfaces);
675  lastface = firstface + numfaces;
676  if (lastface < firstface || lastface > bsp->numfaces) {
677  DEBUG("bad faces");
678  return Q_ERR_BAD_INDEX;
679  }
680  out->firstface = bsp->faces + firstface;
681  out->numfaces = numfaces;
682 
683  out->radius = RadiusFromBounds(out->mins, out->maxs);
684 #endif
685  }
686 
687  return Q_ERR_SUCCESS;
688 }
689 
690 // These are validated after all the areas are loaded
691 LOAD(AreaPortals)
692 {
693  dareaportal_t *in;
694  mareaportal_t *out;
695  int i;
696 
697  bsp->numareaportals = count;
698  bsp->areaportals = ALLOC(sizeof(*out) * count);
699 
700  in = base;
701  out = bsp->areaportals;
702  for (i = 0; i < count; i++, in++, out++) {
703  out->portalnum = LittleLong(in->portalnum);
704  out->otherarea = LittleLong(in->otherarea);
705  }
706 
707  return Q_ERR_SUCCESS;
708 }
709 
710 LOAD(Areas)
711 {
712  darea_t *in;
713  marea_t *out;
714  int i;
715  uint32_t numareaportals, firstareaportal, lastareaportal;
716 
717  bsp->numareas = count;
718  bsp->areas = ALLOC(sizeof(*out) * count);
719 
720  in = base;
721  out = bsp->areas;
722  for (i = 0; i < count; i++, in++, out++) {
723  numareaportals = LittleLong(in->numareaportals);
724  firstareaportal = LittleLong(in->firstareaportal);
725  lastareaportal = firstareaportal + numareaportals;
726  if (lastareaportal < firstareaportal || lastareaportal > bsp->numareaportals) {
727  DEBUG("bad areaportals");
728  return Q_ERR_BAD_INDEX;
729  }
730  out->numareaportals = numareaportals;
731  out->firstareaportal = bsp->areaportals + firstareaportal;
732  out->floodvalid = 0;
733  }
734 
735  return Q_ERR_SUCCESS;
736 }
737 
738 LOAD(EntString)
739 {
740  bsp->numentitychars = count;
741  bsp->entitystring = ALLOC(count + 1);
742  memcpy(bsp->entitystring, base, count);
743  bsp->entitystring[count] = 0;
744 
745  return Q_ERR_SUCCESS;
746 }
747 
748 /*
749 ===============================================================================
750 
751  MAP LOADING
752 
753 ===============================================================================
754 */
755 
756 typedef struct {
757  qerror_t (*load)(bsp_t *, void *, size_t);
758  unsigned lump;
759  size_t disksize;
760  size_t memsize;
761  size_t maxcount;
762 } lump_info_t;
763 
764 #define L(func, lump, disk_t, mem_t) \
765  { BSP_Load##func, LUMP_##lump, sizeof(disk_t), sizeof(mem_t), MAX_MAP_##lump }
766 
767 static const lump_info_t bsp_lumps[] = {
768  L(Visibility, VISIBILITY, byte, byte),
769  L(Texinfo, TEXINFO, dtexinfo_t, mtexinfo_t),
770  L(Planes, PLANES, dplane_t, cplane_t),
771  L(BrushSides, BRUSHSIDES, dbrushside_t, mbrushside_t),
772  L(Brushes, BRUSHES, dbrush_t, mbrush_t),
773  L(LeafBrushes, LEAFBRUSHES, uint16_t, mbrush_t *),
774  L(AreaPortals, AREAPORTALS, dareaportal_t, mareaportal_t),
775  L(Areas, AREAS, darea_t, marea_t),
776 #if USE_REF
777  L(Lightmap, LIGHTING, byte, byte),
778  L(Vertices, VERTEXES, dvertex_t, mvertex_t),
779  L(Edges, EDGES, dedge_t, medge_t),
780  L(SurfEdges, SURFEDGES, uint32_t, msurfedge_t),
781  L(Faces, FACES, dface_t, mface_t),
782  L(LeafFaces, LEAFFACES, uint16_t, mface_t *),
783 #endif
784  L(Leafs, LEAFS, dleaf_t, mleaf_t),
785  L(Nodes, NODES, dnode_t, mnode_t),
786  L(Submodels, MODELS, dmodel_t, mmodel_t),
787  L(EntString, ENTSTRING, char, char),
788  { NULL }
789 };
790 
791 #undef L
792 
793 static list_t bsp_cache;
794 
795 static void BSP_List_f(void)
796 {
797  bsp_t *bsp;
798  size_t bytes;
799 
800  if (LIST_EMPTY(&bsp_cache)) {
801  Com_Printf("BSP cache is empty\n");
802  return;
803  }
804 
805  Com_Printf("------------------\n");
806  bytes = 0;
807 
808  LIST_FOR_EACH(bsp_t, bsp, &bsp_cache, entry) {
809  Com_Printf("%8"PRIz" : %s (%d refs)\n",
810  bsp->hunk.mapped, bsp->name, bsp->refcount);
811  bytes += bsp->hunk.mapped;
812  }
813  Com_Printf("Total resident: %"PRIz"\n", bytes);
814 }
815 
816 static bsp_t *BSP_Find(const char *name)
817 {
818  bsp_t *bsp;
819 
820  LIST_FOR_EACH(bsp_t, bsp, &bsp_cache, entry) {
821  if (!FS_pathcmp(bsp->name, name)) {
822  return bsp;
823  }
824  }
825 
826  return NULL;
827 }
828 
829 static qerror_t BSP_SetParent(mnode_t *node, int key)
830 {
831  mnode_t *child;
832 #if USE_REF
833  mface_t *face;
834  int i;
835 #endif
836 
837  while (node->plane) {
838 #if USE_REF
839  // a face may never belong to more than one node
840  for (i = 0, face = node->firstface; i < node->numfaces; i++, face++) {
841  if (face->drawframe) {
842  DEBUG("duplicate face");
843  return Q_ERR_INFINITE_LOOP;
844  }
845  face->drawframe = key;
846  }
847 #endif
848 
849  child = node->children[0];
850  if (child->parent) {
851  DEBUG("cycle encountered");
852  return Q_ERR_INFINITE_LOOP;
853  }
854  child->parent = node;
855  if (BSP_SetParent(child, key)) {
856  return Q_ERR_INFINITE_LOOP;
857  }
858 
859  child = node->children[1];
860  if (child->parent) {
861  DEBUG("cycle encountered");
862  return Q_ERR_INFINITE_LOOP;
863  }
864  child->parent = node;
865  node = child;
866  }
867 
868  return Q_ERR_SUCCESS;
869 }
870 
871 static qerror_t BSP_ValidateTree(bsp_t *bsp)
872 {
873  mmodel_t *mod;
874  qerror_t ret;
875  int i;
876 #if USE_REF
877  mface_t *face;
878  int j;
879 #endif
880 
881  for (i = 0, mod = bsp->models; i < bsp->nummodels; i++, mod++) {
882  if (i == 0 && mod->headnode != bsp->nodes) {
883  DEBUG("map model 0 headnode is not the first node");
884  return Q_ERR_INVALID_FORMAT;
885  }
886 
887  ret = BSP_SetParent(mod->headnode, ~i);
888  if (ret) {
889  return ret;
890  }
891 
892 #if USE_REF
893  // a face may never belong to more than one model
894  for (j = 0, face = mod->firstface; j < mod->numfaces; j++, face++) {
895  if (face->drawframe && face->drawframe != ~i) {
896  DEBUG("duplicate face");
897  return Q_ERR_INFINITE_LOOP;
898  }
899  face->drawframe = ~i;
900  }
901 #endif
902  }
903 
904  return Q_ERR_SUCCESS;
905 }
906 
907 // also calculates the last portal number used
908 // by CM code to allocate portalopen[] array
909 static qerror_t BSP_ValidateAreaPortals(bsp_t *bsp)
910 {
911  mareaportal_t *p;
912  int i;
913 
914  bsp->lastareaportal = 0;
915  for (i = 0, p = bsp->areaportals; i < bsp->numareaportals; i++, p++) {
916  if (p->portalnum >= MAX_MAP_AREAPORTALS) {
917  DEBUG("bad portalnum");
918  return Q_ERR_TOO_MANY;
919  }
920  if (p->portalnum > bsp->lastareaportal) {
921  bsp->lastareaportal = p->portalnum;
922  }
923  if (p->otherarea >= bsp->numareas) {
924  DEBUG("bad otherarea");
925  return Q_ERR_BAD_INDEX;
926  }
927  }
928 
929  return Q_ERR_SUCCESS;
930 }
931 
932 void BSP_Free(bsp_t *bsp)
933 {
934  if (!bsp) {
935  return;
936  }
937  if (bsp->refcount <= 0) {
938  Com_Error(ERR_FATAL, "%s: negative refcount", __func__);
939  }
940  if (--bsp->refcount == 0) {
941  if (bsp->pvs2_matrix)
942  {
943  // free the PVS2 matrix separately - it's not part of the hunk
944  Z_Free(bsp->pvs2_matrix);
945  bsp->pvs2_matrix = NULL;
946  }
947 
948  Hunk_Free(&bsp->hunk);
949  List_Remove(&bsp->entry);
950  Z_Free(bsp);
951  }
952 }
953 
954 static void BSP_BuildPvsMatrix(bsp_t *bsp)
955 {
956  if (!bsp->vis)
957  return;
958 
959  // a typical map with 2K clusters will take half a megabyte of memory for the matrix
960  size_t matrix_size = bsp->visrowsize * bsp->vis->numclusters;
961 
962  // allocate the matrix but don't set it in the BSP structure yet:
963  // we want BSP_CluterVis to use the old PVS data here, and not the new empty matrix
964  char* pvs_matrix = Z_Mallocz(matrix_size);
965 
966  for (int cluster = 0; cluster < bsp->vis->numclusters; cluster++)
967  {
968  BSP_ClusterVis(bsp, pvs_matrix + bsp->visrowsize * cluster, cluster, DVIS_PVS);
969  }
970 
971  bsp->pvs_matrix = pvs_matrix;
972 }
973 
974 char* BSP_GetPvs(bsp_t *bsp, int cluster)
975 {
976  if (!bsp->vis || !bsp->pvs_matrix)
977  return NULL;
978 
979  if (cluster < 0 || cluster >= bsp->vis->numclusters)
980  return NULL;
981 
982  return bsp->pvs_matrix + bsp->visrowsize * cluster;
983 }
984 
985 char* BSP_GetPvs2(bsp_t *bsp, int cluster)
986 {
987  if (!bsp->vis || !bsp->pvs2_matrix)
988  return NULL;
989 
990  if (cluster < 0 || cluster >= bsp->vis->numclusters)
991  return NULL;
992 
993  return bsp->pvs2_matrix + bsp->visrowsize * cluster;
994 }
995 
996 // Converts `maps/<name>.bsp` into `maps/pvs/<name>.bin`
997 static qboolean BSP_GetPatchedPVSFileName(const char* map_path, char pvs_path[MAX_QPATH])
998 {
999  int path_len = strlen(map_path);
1000  if (path_len < 5 || strcmp(map_path + path_len - 4, ".bsp") != 0)
1001  return qfalse;
1002 
1003  const char* map_file = strrchr(map_path, '/');
1004  if (map_file)
1005  map_file += 1;
1006  else
1007  map_file = map_path;
1008 
1009  memset(pvs_path, 0, MAX_QPATH);
1010  strncpy(pvs_path, map_path, map_file - map_path);
1011  strcat(pvs_path, "pvs/");
1012  strncat(pvs_path, map_file, strlen(map_file) - 4);
1013  strcat(pvs_path, ".bin");
1014 
1015  return qtrue;
1016 }
1017 
1018 // Loads the first- and second-order PVS matrices from a file called `maps/pvs/<mapname>.bin`
1019 static qboolean BSP_LoadPatchedPVS(bsp_t *bsp)
1020 {
1021  char pvs_path[MAX_QPATH];
1022 
1023  if (!BSP_GetPatchedPVSFileName(bsp->name, pvs_path))
1024  return qfalse;
1025 
1026  unsigned char* filebuf = 0;
1027  ssize_t filelen = 0;
1028  filelen = FS_LoadFile(pvs_path, &filebuf);
1029 
1030  if (filebuf == 0)
1031  return qfalse;
1032 
1033  size_t matrix_size = bsp->visrowsize * bsp->vis->numclusters;
1034  if (filelen != matrix_size * 2)
1035  {
1036  FS_FreeFile(filebuf);
1037  return qfalse;
1038  }
1039 
1040  bsp->pvs_matrix = Z_Malloc(matrix_size);
1041  memcpy(bsp->pvs_matrix, filebuf, matrix_size);
1042 
1043  bsp->pvs2_matrix = Z_Malloc(matrix_size);
1044  memcpy(bsp->pvs2_matrix, filebuf + matrix_size, matrix_size);
1045 
1046  FS_FreeFile(filebuf);
1047  return qtrue;
1048 }
1049 
1050 // Saves the first- and second-order PVS matrices to a file called `maps/pvs/<mapname>.bin`
1051 qboolean BSP_SavePatchedPVS(bsp_t *bsp)
1052 {
1053  char pvs_path[MAX_QPATH];
1054 
1055  if (!BSP_GetPatchedPVSFileName(bsp->name, pvs_path))
1056  return qfalse;
1057 
1058  if (!bsp->pvs_matrix)
1059  return qfalse;
1060 
1061  if (!bsp->pvs2_matrix)
1062  return qfalse;
1063 
1064  size_t matrix_size = bsp->visrowsize * bsp->vis->numclusters;
1065  unsigned char* filebuf = Z_Malloc(matrix_size * 2);
1066 
1067  memcpy(filebuf, bsp->pvs_matrix, matrix_size);
1068  memcpy(filebuf + matrix_size, bsp->pvs2_matrix, matrix_size);
1069 
1070  qerror_t err = FS_WriteFile(pvs_path, filebuf, matrix_size * 2);
1071 
1072  Z_Free(filebuf);
1073 
1074  if (err >= 0)
1075  return qtrue;
1076  else
1077  return qfalse;
1078 }
1079 
1080 /*
1081 ==================
1082 BSP_Load
1083 
1084 Loads in the map and all submodels
1085 ==================
1086 */
1087 qerror_t BSP_Load(const char *name, bsp_t **bsp_p)
1088 {
1089  bsp_t *bsp;
1090  byte *buf;
1091  dheader_t *header;
1092  const lump_info_t *info;
1093  size_t filelen, ofs, len, end, count;
1094  qerror_t ret;
1095  byte *lumpdata[HEADER_LUMPS];
1096  size_t lumpcount[HEADER_LUMPS];
1097  size_t memsize;
1098 
1099  if (!name || !bsp_p)
1100  Com_Error(ERR_FATAL, "%s: NULL", __func__);
1101 
1102  *bsp_p = NULL;
1103 
1104  if (!*name)
1105  return Q_ERR_NOENT;
1106 
1107  if ((bsp = BSP_Find(name)) != NULL) {
1108  Com_PageInMemory(bsp->hunk.base, bsp->hunk.cursize);
1109  bsp->refcount++;
1110  *bsp_p = bsp;
1111  return Q_ERR_SUCCESS;
1112  }
1113 
1114  //
1115  // load the file
1116  //
1117  filelen = FS_LoadFile(name, (void **)&buf);
1118  if (!buf) {
1119  return filelen;
1120  }
1121 
1122  // byte swap and validate the header
1123  header = (dheader_t *)buf;
1124  if (LittleLong(header->ident) != IDBSPHEADER) {
1125  ret = Q_ERR_UNKNOWN_FORMAT;
1126  goto fail2;
1127  }
1128  if (LittleLong(header->version) != BSPVERSION) {
1129  ret = Q_ERR_UNKNOWN_FORMAT;
1130  goto fail2;
1131  }
1132 
1133  // byte swap and validate all lumps
1134  memsize = 0;
1135  for (info = bsp_lumps; info->load; info++) {
1136  ofs = LittleLong(header->lumps[info->lump].fileofs);
1137  len = LittleLong(header->lumps[info->lump].filelen);
1138  end = ofs + len;
1139  if (end < ofs || end > filelen) {
1140  ret = Q_ERR_BAD_EXTENT;
1141  goto fail2;
1142  }
1143  if (len % info->disksize) {
1144  ret = Q_ERR_ODD_SIZE;
1145  goto fail2;
1146  }
1147  count = len / info->disksize;
1148  if (count > info->maxcount) {
1149  ret = Q_ERR_TOO_MANY;
1150  goto fail2;
1151  }
1152 
1153  lumpdata[info->lump] = buf + ofs;
1154  lumpcount[info->lump] = count;
1155 
1156  memsize += count * info->memsize;
1157  }
1158 
1159  // load into hunk
1160  len = strlen(name);
1161  bsp = Z_Mallocz(sizeof(*bsp) + len);
1162  memcpy(bsp->name, name, len + 1);
1163  bsp->refcount = 1;
1164 
1165  // add an extra page for cacheline alignment overhead
1166  Hunk_Begin(&bsp->hunk, memsize + 4096);
1167 
1168  // calculate the checksum
1169  bsp->checksum = LittleLong(Com_BlockChecksum(buf, filelen));
1170 
1171  // load all lumps
1172  for (info = bsp_lumps; info->load; info++) {
1173  ret = info->load(bsp, lumpdata[info->lump], lumpcount[info->lump]);
1174  if (ret) {
1175  goto fail1;
1176  }
1177  }
1178 
1179  ret = BSP_ValidateAreaPortals(bsp);
1180  if (ret) {
1181  goto fail1;
1182  }
1183 
1184  ret = BSP_ValidateTree(bsp);
1185  if (ret) {
1186  goto fail1;
1187  }
1188 
1189  if (!BSP_LoadPatchedPVS(bsp))
1190  {
1191  if (dedicated->integer)
1192  Com_WPrintf("WARNING: Pathced PVS file for %s unavailable. Some entities may disappear.\n"
1193  "Load the map with the RTX renderer once to generate the patched PVS file.\n", bsp->name);
1194  else
1195  BSP_BuildPvsMatrix(bsp);
1196  }
1197  else
1198  {
1199  bsp->pvs_patched = qtrue;
1200  }
1201 
1202  Hunk_End(&bsp->hunk);
1203 
1204  List_Append(&bsp_cache, &bsp->entry);
1205 
1206  FS_FreeFile(buf);
1207 
1208  *bsp_p = bsp;
1209  return Q_ERR_SUCCESS;
1210 
1211 fail1:
1212  Hunk_Free(&bsp->hunk);
1213  Z_Free(bsp);
1214 fail2:
1215  FS_FreeFile(buf);
1216  return ret;
1217 }
1218 
1219 /*
1220 ===============================================================================
1221 
1222 HELPER FUNCTIONS
1223 
1224 ===============================================================================
1225 */
1226 
1227 #if USE_REF
1228 
1229 static lightpoint_t *light_point;
1230 
1231 static qboolean BSP_RecursiveLightPoint(mnode_t *node, float p1f, float p2f, vec3_t p1, vec3_t p2)
1232 {
1233  vec_t d1, d2, frac, midf;
1234  vec3_t mid;
1235  int i, side, s, t;
1236  mface_t *surf;
1237  mtexinfo_t *texinfo;
1238 
1239  while (node->plane) {
1240  // calculate distancies
1241  d1 = PlaneDiffFast(p1, node->plane);
1242  d2 = PlaneDiffFast(p2, node->plane);
1243  side = (d1 < 0);
1244 
1245  if ((d2 < 0) == side) {
1246  // both points are one the same side
1247  node = node->children[side];
1248  continue;
1249  }
1250 
1251  // find crossing point
1252  frac = d1 / (d1 - d2);
1253  midf = p1f + (p2f - p1f) * frac;
1254  LerpVector(p1, p2, frac, mid);
1255 
1256  // check near side
1257  if (BSP_RecursiveLightPoint(node->children[side], p1f, midf, p1, mid))
1258  return qtrue;
1259 
1260  for (i = 0, surf = node->firstface; i < node->numfaces; i++, surf++) {
1261  if (!surf->lightmap)
1262  continue;
1263 
1264  texinfo = surf->texinfo;
1265  if (texinfo->c.flags & SURF_NOLM_MASK)
1266  continue;
1267 
1268  s = DotProduct(texinfo->axis[0], mid) + texinfo->offset[0];
1269  t = DotProduct(texinfo->axis[1], mid) + texinfo->offset[1];
1270 
1271  s -= surf->texturemins[0];
1272  t -= surf->texturemins[1];
1273  if (s < 0 || s > surf->extents[0])
1274  continue;
1275  if (t < 0 || t > surf->extents[1])
1276  continue;
1277 
1278  light_point->surf = surf;
1279  light_point->plane = *surf->plane;
1280  light_point->s = s;
1281  light_point->t = t;
1282  light_point->fraction = midf;
1283  return qtrue;
1284  }
1285 
1286  // check far side
1287  return BSP_RecursiveLightPoint(node->children[side ^ 1], midf, p2f, mid, p2);
1288  }
1289 
1290  return qfalse;
1291 }
1292 
1293 void BSP_LightPoint(lightpoint_t *point, vec3_t start, vec3_t end, mnode_t *headnode)
1294 {
1295  light_point = point;
1296  light_point->surf = NULL;
1297  light_point->fraction = 1;
1298 
1299  BSP_RecursiveLightPoint(headnode, 0, 1, start, end);
1300 }
1301 
1302 void BSP_TransformedLightPoint(lightpoint_t *point, vec3_t start, vec3_t end,
1303  mnode_t *headnode, vec3_t origin, vec3_t angles)
1304 {
1305  vec3_t start_l, end_l;
1306  vec3_t axis[3];
1307 
1308  light_point = point;
1309  light_point->surf = NULL;
1310  light_point->fraction = 1;
1311 
1312  // subtract origin offset
1313  VectorSubtract(start, origin, start_l);
1314  VectorSubtract(end, origin, end_l);
1315 
1316  // rotate start and end into the models frame of reference
1317  if (angles) {
1318  AnglesToAxis(angles, axis);
1319  RotatePoint(start_l, axis);
1320  RotatePoint(end_l, axis);
1321  }
1322 
1323  // sweep the line through the model
1324  if (!BSP_RecursiveLightPoint(headnode, 0, 1, start_l, end_l))
1325  return;
1326 
1327  // rotate plane normal into the worlds frame of reference
1328  if (angles) {
1329  TransposeAxis(axis);
1330  RotatePoint(point->plane.normal, axis);
1331  }
1332 
1333  // offset plane distance
1334  point->plane.dist += DotProduct(point->plane.normal, origin);
1335 }
1336 
1337 #endif
1338 
1339 byte *BSP_ClusterVis(bsp_t *bsp, byte *mask, int cluster, int vis)
1340 {
1341  byte *in, *out, *in_end, *out_end;
1342  int c;
1343 
1344  if (!bsp || !bsp->vis) {
1345  return memset(mask, 0xff, VIS_MAX_BYTES);
1346  }
1347  if (cluster == -1) {
1348  return memset(mask, 0, bsp->visrowsize);
1349  }
1350  if (cluster < 0 || cluster >= bsp->vis->numclusters) {
1351  Com_Error(ERR_DROP, "%s: bad cluster", __func__);
1352  }
1353 
1354  if (vis == DVIS_PVS2)
1355  {
1356  if (bsp->pvs2_matrix)
1357  {
1358  char* row = BSP_GetPvs2(bsp, cluster);
1359  memcpy(mask, row, bsp->visrowsize);
1360  return mask;
1361  }
1362 
1363  // fallback
1364  vis = DVIS_PVS;
1365  }
1366 
1367  if (vis == DVIS_PVS && bsp->pvs_matrix)
1368  {
1369  char* row = BSP_GetPvs(bsp, cluster);
1370  memcpy(mask, row, bsp->visrowsize);
1371  return mask;
1372  }
1373 
1374  // decompress vis
1375  in_end = (byte *)bsp->vis + bsp->numvisibility;
1376  in = (byte *)bsp->vis + bsp->vis->bitofs[cluster][vis];
1377  out_end = mask + bsp->visrowsize;
1378  out = mask;
1379  do {
1380  if (in >= in_end) {
1381  goto overrun;
1382  }
1383  if (*in) {
1384  *out++ = *in++;
1385  continue;
1386  }
1387 
1388  if (in + 1 >= in_end) {
1389  goto overrun;
1390  }
1391  c = in[1];
1392  in += 2;
1393  if (out + c > out_end) {
1394 overrun:
1395  c = out_end - out;
1396  }
1397  while (c--) {
1398  *out++ = 0;
1399  }
1400  } while (out < out_end);
1401 
1402  // apply our ugly PVS patches
1403  if (map_visibility_patch->integer) {
1404  if (bsp->checksum == 0x1e5b50c5) {
1405  // q2dm3, pent bridge
1406  if (cluster == 345 || cluster == 384) {
1407  Q_SetBit(mask, 466);
1408  Q_SetBit(mask, 484);
1409  Q_SetBit(mask, 692);
1410  }
1411  } else if (bsp->checksum == 0x04cfa792) {
1412  // q2dm1, above lower RL
1413  if (cluster == 395) {
1414  Q_SetBit(mask, 176);
1415  Q_SetBit(mask, 183);
1416  }
1417  } else if (bsp->checksum == 0x2c3ab9b0) {
1418  // q2dm8, CG/RG area
1419  if (cluster == 629 || cluster == 631 ||
1420  cluster == 633 || cluster == 639) {
1421  Q_SetBit(mask, 908);
1422  Q_SetBit(mask, 909);
1423  Q_SetBit(mask, 910);
1424  Q_SetBit(mask, 915);
1425  Q_SetBit(mask, 923);
1426  Q_SetBit(mask, 924);
1427  Q_SetBit(mask, 927);
1428  Q_SetBit(mask, 930);
1429  Q_SetBit(mask, 938);
1430  Q_SetBit(mask, 939);
1431  Q_SetBit(mask, 947);
1432  }
1433  }
1434  }
1435 
1436  return mask;
1437 }
1438 
1439 mleaf_t *BSP_PointLeaf(mnode_t *node, vec3_t p)
1440 {
1441  float d;
1442 
1443  while (node->plane) {
1444  d = PlaneDiffFast(p, node->plane);
1445  if (d < 0)
1446  node = node->children[1];
1447  else
1448  node = node->children[0];
1449  }
1450 
1451  return (mleaf_t *)node;
1452 }
1453 
1454 /*
1455 ==================
1456 BSP_InlineModel
1457 ==================
1458 */
1459 mmodel_t *BSP_InlineModel(bsp_t *bsp, const char *name)
1460 {
1461  int num;
1462 
1463  if (!bsp || !name) {
1464  Com_Error(ERR_DROP, "%s: NULL", __func__);
1465  }
1466  if (name[0] != '*') {
1467  Com_Error(ERR_DROP, "%s: bad name: %s", __func__, name);
1468  }
1469  num = atoi(name + 1);
1470  if (num < 1 || num >= bsp->nummodels) {
1471  Com_Error(ERR_DROP, "%s: bad number: %d", __func__, num);
1472  }
1473 
1474  return &bsp->models[num];
1475 }
1476 
1477 void BSP_Init(void)
1478 {
1479  map_visibility_patch = Cvar_Get("map_visibility_patch", "1", 0);
1480 
1481  Cmd_AddCommand("bsplist", BSP_List_f);
1482 
1483  List_Init(&bsp_cache);
1484 }
1485 
RadiusFromBounds
vec_t RadiusFromBounds(const vec3_t mins, const vec3_t maxs)
Definition: shared.c:127
BSP_LoadPatchedPVS
static qboolean BSP_LoadPatchedPVS(bsp_t *bsp)
Definition: bsp.c:1019
BSP_GetPvs2
char * BSP_GetPvs2(bsp_t *bsp, int cluster)
Definition: bsp.c:985
Com_BlockChecksum
uint32_t Com_BlockChecksum(void *buffer, size_t len)
Definition: mdfour.c:187
Hunk_Begin
void Hunk_Begin(memhunk_t *hunk, size_t maxsize)
Definition: hunk.c:23
Cmd_AddCommand
void Cmd_AddCommand(const char *name, xcommand_t function)
Definition: cmd.c:1562
Cvar_Get
cvar_t * Cvar_Get(const char *var_name, const char *var_value, int flags)
Definition: cvar.c:257
BSP_ClusterVis
byte * BSP_ClusterVis(bsp_t *bsp, byte *mask, int cluster, int vis)
Definition: bsp.c:1339
bsp_cache
static list_t bsp_cache
Definition: bsp.c:793
lump_info_t
Definition: bsp.c:756
FS_WriteFile
qerror_t FS_WriteFile(const char *path, const void *data, size_t len)
Definition: files.c:1932
BSP_GetPatchedPVSFileName
static qboolean BSP_GetPatchedPVSFileName(const char *map_path, char pvs_path[MAX_QPATH])
Definition: bsp.c:997
BSP_SavePatchedPVS
qboolean BSP_SavePatchedPVS(bsp_t *bsp)
Definition: bsp.c:1051
BSP_Init
void BSP_Init(void)
Definition: bsp.c:1477
BSP_InlineModel
mmodel_t * BSP_InlineModel(bsp_t *bsp, const char *name)
Definition: bsp.c:1459
BSP_PointLeaf
mleaf_t * BSP_PointLeaf(mnode_t *node, vec3_t p)
Definition: bsp.c:1439
lump_info_t::lump
unsigned lump
Definition: bsp.c:758
Hunk_Free
void Hunk_Free(memhunk_t *hunk)
Definition: hunk.c:75
Com_Error
void Com_Error(error_type_t type, const char *fmt,...)
Definition: g_main.c:258
BSP_Free
void BSP_Free(bsp_t *bsp)
Definition: bsp.c:932
dedicated
cvar_t * dedicated
Definition: g_main.c:46
BSP_SetParent
static qerror_t BSP_SetParent(mnode_t *node, int key)
Definition: bsp.c:829
load
qerror_t(* load)(byte *, size_t, image_t *, byte **)
Definition: images.c:658
BSP_List_f
static void BSP_List_f(void)
Definition: bsp.c:795
Z_Free
void Z_Free(void *ptr)
Definition: zone.c:147
nulltexinfo
mtexinfo_t nulltexinfo
Definition: cmodel.c:30
lump_info_t::load
qerror_t(* load)(bsp_t *, void *, size_t)
Definition: bsp.c:757
origin
static vec3_t origin
Definition: mesh.c:27
Hunk_End
void Hunk_End(memhunk_t *hunk)
Definition: hunk.c:66
BSP_ValidateAreaPortals
static qerror_t BSP_ValidateAreaPortals(bsp_t *bsp)
Definition: bsp.c:909
lump_info_t::disksize
size_t disksize
Definition: bsp.c:759
SetPlaneSignbits
void SetPlaneSignbits(cplane_t *plane)
Definition: math.c:298
bsp_lumps
static const lump_info_t bsp_lumps[]
Definition: bsp.c:767
lump_info_t::memsize
size_t memsize
Definition: bsp.c:760
lump_info_t::maxcount
size_t maxcount
Definition: bsp.c:761
c
statCounters_t c
Definition: main.c:30
map_visibility_patch
static cvar_t * map_visibility_patch
Definition: bsp.c:37
BSP_Find
static bsp_t * BSP_Find(const char *name)
Definition: bsp.c:816
BSP_BuildPvsMatrix
static void BSP_BuildPvsMatrix(bsp_t *bsp)
Definition: bsp.c:954
err
int err
Definition: win.h:24
LOAD
#define LOAD(func)
Definition: bsp.c:50
SetPlaneType
void SetPlaneType(cplane_t *plane)
Definition: math.c:278
BSP_Load
qerror_t BSP_Load(const char *name, bsp_t **bsp_p)
Definition: bsp.c:1087
DEBUG
#define DEBUG(msg)
Definition: bsp.c:53
Com_PageInMemory
void Com_PageInMemory(void *buffer, size_t size)
Definition: utils.c:383
BSP_GetPvs
char * BSP_GetPvs(bsp_t *bsp, int cluster)
Definition: bsp.c:974
ALLOC
#define ALLOC(size)
Definition: bsp.c:47
BSP_ValidateTree
static qerror_t BSP_ValidateTree(bsp_t *bsp)
Definition: bsp.c:871
L
#define L(func, lump, disk_t, mem_t)
Definition: bsp.c:764