]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - model_brush.c
renamed varray_ arrays to rsurface_array_, and they are no longer used outside the...
[xonotic/darkplaces.git] / model_brush.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (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.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22 #include "image.h"
23 #include "r_shadow.h"
24 #include "polygon.h"
25 #include "curves.h"
26 #include "wad.h"
27
28
29 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128", "how large water polygons should be (smaller values produce more polygons which give better warping effects)"};
30 cvar_t halflifebsp = {0, "halflifebsp", "0", "indicates the current map is hlbsp format (useful to know because of different bounding box sizes)"};
31 cvar_t mcbsp = {0, "mcbsp", "0", "indicates the current map is mcbsp format (useful to know because of different bounding box sizes)"};
32 cvar_t r_novis = {0, "r_novis", "0", "draws whole level, see also sv_cullentities_pvs 0"};
33 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0", "mipmaps lightmaps on upload, also expanding them to power of 2 sizes, this runs slower"};
34 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1", "whether to use RGBA (32bit) or RGB (24bit) lightmaps"};
35 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0", "pretends there was no texture lump found in the q1bsp/hlbsp loading (useful for debugging this rare case)"};
36 cvar_t r_subdivisions_tolerance = {0, "r_subdivisions_tolerance", "4", "maximum error tolerance on curve subdivision for rendering purposes (in other words, the curves will be given as many polygons as necessary to represent curves at this quality)"};
37 cvar_t r_subdivisions_mintess = {0, "r_subdivisions_mintess", "1", "minimum number of subdivisions (values above 1 will smooth curves that don't need it)"};
38 cvar_t r_subdivisions_maxtess = {0, "r_subdivisions_maxtess", "1024", "maximum number of subdivisions (prevents curves beyond a certain detail level, limits smoothing)"};
39 cvar_t r_subdivisions_maxvertices = {0, "r_subdivisions_maxvertices", "65536", "maximum vertices allowed per subdivided curve"};
40 cvar_t r_subdivisions_collision_tolerance = {0, "r_subdivisions_collision_tolerance", "15", "maximum error tolerance on curve subdivision for collision purposes (usually a larger error tolerance than for rendering)"};
41 cvar_t r_subdivisions_collision_mintess = {0, "r_subdivisions_collision_mintess", "1", "minimum number of subdivisions (values above 1 will smooth curves that don't need it)"};
42 cvar_t r_subdivisions_collision_maxtess = {0, "r_subdivisions_collision_maxtess", "1024", "maximum number of subdivisions (prevents curves beyond a certain detail level, limits smoothing)"};
43 cvar_t r_subdivisions_collision_maxvertices = {0, "r_subdivisions_collision_maxvertices", "4225", "maximum vertices allowed per subdivided curve"};
44 cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1", "enables collisions with curves (SLOW)"};
45 cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "1", "whether to use optimized traceline code for line traces (as opposed to tracebox code)"};
46 cvar_t mod_q3bsp_debugtracebrush = {0, "mod_q3bsp_debugtracebrush", "0", "selects different tracebrush bsp recursion algorithms (for debugging purposes only)"};
47
48 void Mod_BrushInit(void)
49 {
50 //      Cvar_RegisterVariable(&r_subdivide_size);
51         Cvar_RegisterVariable(&halflifebsp);
52         Cvar_RegisterVariable(&mcbsp);
53         Cvar_RegisterVariable(&r_novis);
54         Cvar_RegisterVariable(&r_miplightmaps);
55         Cvar_RegisterVariable(&r_lightmaprgba);
56         Cvar_RegisterVariable(&r_nosurftextures);
57         Cvar_RegisterVariable(&r_subdivisions_tolerance);
58         Cvar_RegisterVariable(&r_subdivisions_mintess);
59         Cvar_RegisterVariable(&r_subdivisions_maxtess);
60         Cvar_RegisterVariable(&r_subdivisions_maxvertices);
61         Cvar_RegisterVariable(&r_subdivisions_collision_tolerance);
62         Cvar_RegisterVariable(&r_subdivisions_collision_mintess);
63         Cvar_RegisterVariable(&r_subdivisions_collision_maxtess);
64         Cvar_RegisterVariable(&r_subdivisions_collision_maxvertices);
65         Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
66         Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
67         Cvar_RegisterVariable(&mod_q3bsp_debugtracebrush);
68 }
69
70 static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
71 {
72         mnode_t *node;
73
74         if (model == NULL)
75                 return NULL;
76
77         // LordHavoc: modified to start at first clip node,
78         // in other words: first node of the (sub)model
79         node = model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode;
80         while (node->plane)
81                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
82
83         return (mleaf_t *)node;
84 }
85
86 static void Mod_Q1BSP_AmbientSoundLevelsForPoint(model_t *model, const vec3_t p, unsigned char *out, int outsize)
87 {
88         int i;
89         mleaf_t *leaf;
90         leaf = Mod_Q1BSP_PointInLeaf(model, p);
91         if (leaf)
92         {
93                 i = min(outsize, (int)sizeof(leaf->ambient_sound_level));
94                 if (i)
95                 {
96                         memcpy(out, leaf->ambient_sound_level, i);
97                         out += i;
98                         outsize -= i;
99                 }
100         }
101         if (outsize)
102                 memset(out, 0, outsize);
103 }
104
105 static int Mod_Q1BSP_FindBoxClusters(model_t *model, const vec3_t mins, const vec3_t maxs, int maxclusters, int *clusterlist)
106 {
107         int numclusters = 0;
108         int nodestackindex = 0;
109         mnode_t *node, *nodestack[1024];
110         if (!model->brush.num_pvsclusters)
111                 return -1;
112         node = model->brush.data_nodes;
113         for (;;)
114         {
115 #if 1
116                 if (node->plane)
117                 {
118                         // node - recurse down the BSP tree
119                         int sides = BoxOnPlaneSide(mins, maxs, node->plane);
120                         if (sides < 3)
121                         {
122                                 if (sides == 0)
123                                         return -1; // ERROR: NAN bounding box!
124                                 // box is on one side of plane, take that path
125                                 node = node->children[sides-1];
126                         }
127                         else
128                         {
129                                 // box crosses plane, take one path and remember the other
130                                 if (nodestackindex < 1024)
131                                         nodestack[nodestackindex++] = node->children[0];
132                                 node = node->children[1];
133                         }
134                         continue;
135                 }
136                 else
137                 {
138                         // leaf - add clusterindex to list
139                         if (numclusters < maxclusters)
140                                 clusterlist[numclusters] = ((mleaf_t *)node)->clusterindex;
141                         numclusters++;
142                 }
143 #else
144                 if (BoxesOverlap(mins, maxs, node->mins, node->maxs))
145                 {
146                         if (node->plane)
147                         {
148                                 if (nodestackindex < 1024)
149                                         nodestack[nodestackindex++] = node->children[0];
150                                 node = node->children[1];
151                                 continue;
152                         }
153                         else
154                         {
155                                 // leaf - add clusterindex to list
156                                 if (numclusters < maxclusters)
157                                         clusterlist[numclusters] = ((mleaf_t *)node)->clusterindex;
158                                 numclusters++;
159                         }
160                 }
161 #endif
162                 // try another path we didn't take earlier
163                 if (nodestackindex == 0)
164                         break;
165                 node = nodestack[--nodestackindex];
166         }
167         // return number of clusters found (even if more than the maxclusters)
168         return numclusters;
169 }
170
171 static int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const unsigned char *pvs, const vec3_t mins, const vec3_t maxs)
172 {
173         int nodestackindex = 0;
174         mnode_t *node, *nodestack[1024];
175         if (!model->brush.num_pvsclusters)
176                 return true;
177         node = model->brush.data_nodes;
178         for (;;)
179         {
180 #if 1
181                 if (node->plane)
182                 {
183                         // node - recurse down the BSP tree
184                         int sides = BoxOnPlaneSide(mins, maxs, node->plane);
185                         if (sides < 3)
186                         {
187                                 if (sides == 0)
188                                         return -1; // ERROR: NAN bounding box!
189                                 // box is on one side of plane, take that path
190                                 node = node->children[sides-1];
191                         }
192                         else
193                         {
194                                 // box crosses plane, take one path and remember the other
195                                 if (nodestackindex < 1024)
196                                         nodestack[nodestackindex++] = node->children[0];
197                                 node = node->children[1];
198                         }
199                         continue;
200                 }
201                 else
202                 {
203                         // leaf - check cluster bit
204                         int clusterindex = ((mleaf_t *)node)->clusterindex;
205                         if (CHECKPVSBIT(pvs, clusterindex))
206                         {
207                                 // it is visible, return immediately with the news
208                                 return true;
209                         }
210                 }
211 #else
212                 if (BoxesOverlap(mins, maxs, node->mins, node->maxs))
213                 {
214                         if (node->plane)
215                         {
216                                 if (nodestackindex < 1024)
217                                         nodestack[nodestackindex++] = node->children[0];
218                                 node = node->children[1];
219                                 continue;
220                         }
221                         else
222                         {
223                                 // leaf - check cluster bit
224                                 int clusterindex = ((mleaf_t *)node)->clusterindex;
225                                 if (CHECKPVSBIT(pvs, clusterindex))
226                                 {
227                                         // it is visible, return immediately with the news
228                                         return true;
229                                 }
230                         }
231                 }
232 #endif
233                 // nothing to see here, try another path we didn't take earlier
234                 if (nodestackindex == 0)
235                         break;
236                 node = nodestack[--nodestackindex];
237         }
238         // it is not visible
239         return false;
240 }
241
242 static int Mod_Q1BSP_BoxTouchingLeafPVS(model_t *model, const unsigned char *pvs, const vec3_t mins, const vec3_t maxs)
243 {
244         int nodestackindex = 0;
245         mnode_t *node, *nodestack[1024];
246         if (!model->brush.num_leafs)
247                 return true;
248         node = model->brush.data_nodes;
249         for (;;)
250         {
251 #if 1
252                 if (node->plane)
253                 {
254                         // node - recurse down the BSP tree
255                         int sides = BoxOnPlaneSide(mins, maxs, node->plane);
256                         if (sides < 3)
257                         {
258                                 if (sides == 0)
259                                         return -1; // ERROR: NAN bounding box!
260                                 // box is on one side of plane, take that path
261                                 node = node->children[sides-1];
262                         }
263                         else
264                         {
265                                 // box crosses plane, take one path and remember the other
266                                 if (nodestackindex < 1024)
267                                         nodestack[nodestackindex++] = node->children[0];
268                                 node = node->children[1];
269                         }
270                         continue;
271                 }
272                 else
273                 {
274                         // leaf - check cluster bit
275                         int clusterindex = ((mleaf_t *)node) - model->brush.data_leafs;
276                         if (CHECKPVSBIT(pvs, clusterindex))
277                         {
278                                 // it is visible, return immediately with the news
279                                 return true;
280                         }
281                 }
282 #else
283                 if (BoxesOverlap(mins, maxs, node->mins, node->maxs))
284                 {
285                         if (node->plane)
286                         {
287                                 if (nodestackindex < 1024)
288                                         nodestack[nodestackindex++] = node->children[0];
289                                 node = node->children[1];
290                                 continue;
291                         }
292                         else
293                         {
294                                 // leaf - check cluster bit
295                                 int clusterindex = ((mleaf_t *)node) - model->brush.data_leafs;
296                                 if (CHECKPVSBIT(pvs, clusterindex))
297                                 {
298                                         // it is visible, return immediately with the news
299                                         return true;
300                                 }
301                         }
302                 }
303 #endif
304                 // nothing to see here, try another path we didn't take earlier
305                 if (nodestackindex == 0)
306                         break;
307                 node = nodestack[--nodestackindex];
308         }
309         // it is not visible
310         return false;
311 }
312
313 static int Mod_Q1BSP_BoxTouchingVisibleLeafs(model_t *model, const unsigned char *visibleleafs, const vec3_t mins, const vec3_t maxs)
314 {
315         int nodestackindex = 0;
316         mnode_t *node, *nodestack[1024];
317         if (!model->brush.num_leafs)
318                 return true;
319         node = model->brush.data_nodes;
320         for (;;)
321         {
322 #if 1
323                 if (node->plane)
324                 {
325                         // node - recurse down the BSP tree
326                         int sides = BoxOnPlaneSide(mins, maxs, node->plane);
327                         if (sides < 3)
328                         {
329                                 if (sides == 0)
330                                         return -1; // ERROR: NAN bounding box!
331                                 // box is on one side of plane, take that path
332                                 node = node->children[sides-1];
333                         }
334                         else
335                         {
336                                 // box crosses plane, take one path and remember the other
337                                 if (nodestackindex < 1024)
338                                         nodestack[nodestackindex++] = node->children[0];
339                                 node = node->children[1];
340                         }
341                         continue;
342                 }
343                 else
344                 {
345                         // leaf - check if it is visible
346                         if (visibleleafs[(mleaf_t *)node - model->brush.data_leafs])
347                         {
348                                 // it is visible, return immediately with the news
349                                 return true;
350                         }
351                 }
352 #else
353                 if (BoxesOverlap(mins, maxs, node->mins, node->maxs))
354                 {
355                         if (node->plane)
356                         {
357                                 if (nodestackindex < 1024)
358                                         nodestack[nodestackindex++] = node->children[0];
359                                 node = node->children[1];
360                                 continue;
361                         }
362                         else
363                         {
364                                 // leaf - check if it is visible
365                                 if (visibleleafs[(mleaf_t *)node - model->brush.data_leafs])
366                                 {
367                                         // it is visible, return immediately with the news
368                                         return true;
369                                 }
370                         }
371                 }
372 #endif
373                 // nothing to see here, try another path we didn't take earlier
374                 if (nodestackindex == 0)
375                         break;
376                 node = nodestack[--nodestackindex];
377         }
378         // it is not visible
379         return false;
380 }
381
382 typedef struct findnonsolidlocationinfo_s
383 {
384         vec3_t center;
385         vec_t radius;
386         vec3_t nudge;
387         vec_t bestdist;
388         model_t *model;
389 }
390 findnonsolidlocationinfo_t;
391
392 static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
393 {
394         int i, surfacenum, k, *tri, *mark;
395         float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
396         msurface_t *surface;
397         for (surfacenum = 0, mark = leaf->firstleafsurface;surfacenum < leaf->numleafsurfaces;surfacenum++, mark++)
398         {
399                 surface = info->model->data_surfaces + *mark;
400                 if (surface->texture->supercontents & SUPERCONTENTS_SOLID)
401                 {
402                         for (k = 0;k < surface->num_triangles;k++)
403                         {
404                                 tri = (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle) + k * 3;
405                                 VectorCopy((surface->groupmesh->data_vertex3f + tri[0] * 3), vert[0]);
406                                 VectorCopy((surface->groupmesh->data_vertex3f + tri[1] * 3), vert[1]);
407                                 VectorCopy((surface->groupmesh->data_vertex3f + tri[2] * 3), vert[2]);
408                                 VectorSubtract(vert[1], vert[0], edge[0]);
409                                 VectorSubtract(vert[2], vert[1], edge[1]);
410                                 CrossProduct(edge[1], edge[0], facenormal);
411                                 if (facenormal[0] || facenormal[1] || facenormal[2])
412                                 {
413                                         VectorNormalize(facenormal);
414                                         f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
415                                         if (f <= info->bestdist && f >= -info->bestdist)
416                                         {
417                                                 VectorSubtract(vert[0], vert[2], edge[2]);
418                                                 VectorNormalize(edge[0]);
419                                                 VectorNormalize(edge[1]);
420                                                 VectorNormalize(edge[2]);
421                                                 CrossProduct(facenormal, edge[0], edgenormal[0]);
422                                                 CrossProduct(facenormal, edge[1], edgenormal[1]);
423                                                 CrossProduct(facenormal, edge[2], edgenormal[2]);
424                                                 // face distance
425                                                 if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
426                                                  && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
427                                                  && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
428                                                 {
429                                                         // we got lucky, the center is within the face
430                                                         dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
431                                                         if (dist < 0)
432                                                         {
433                                                                 dist = -dist;
434                                                                 if (info->bestdist > dist)
435                                                                 {
436                                                                         info->bestdist = dist;
437                                                                         VectorScale(facenormal, (info->radius - -dist), info->nudge);
438                                                                 }
439                                                         }
440                                                         else
441                                                         {
442                                                                 if (info->bestdist > dist)
443                                                                 {
444                                                                         info->bestdist = dist;
445                                                                         VectorScale(facenormal, (info->radius - dist), info->nudge);
446                                                                 }
447                                                         }
448                                                 }
449                                                 else
450                                                 {
451                                                         // check which edge or vertex the center is nearest
452                                                         for (i = 0;i < 3;i++)
453                                                         {
454                                                                 f = DotProduct(info->center, edge[i]);
455                                                                 if (f >= DotProduct(vert[0], edge[i])
456                                                                  && f <= DotProduct(vert[1], edge[i]))
457                                                                 {
458                                                                         // on edge
459                                                                         VectorMA(info->center, -f, edge[i], point);
460                                                                         dist = sqrt(DotProduct(point, point));
461                                                                         if (info->bestdist > dist)
462                                                                         {
463                                                                                 info->bestdist = dist;
464                                                                                 VectorScale(point, (info->radius / dist), info->nudge);
465                                                                         }
466                                                                         // skip both vertex checks
467                                                                         // (both are further away than this edge)
468                                                                         i++;
469                                                                 }
470                                                                 else
471                                                                 {
472                                                                         // not on edge, check first vertex of edge
473                                                                         VectorSubtract(info->center, vert[i], point);
474                                                                         dist = sqrt(DotProduct(point, point));
475                                                                         if (info->bestdist > dist)
476                                                                         {
477                                                                                 info->bestdist = dist;
478                                                                                 VectorScale(point, (info->radius / dist), info->nudge);
479                                                                         }
480                                                                 }
481                                                         }
482                                                 }
483                                         }
484                                 }
485                         }
486                 }
487         }
488 }
489
490 static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
491 {
492         if (node->plane)
493         {
494                 float f = PlaneDiff(info->center, node->plane);
495                 if (f >= -info->bestdist)
496                         Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[0]);
497                 if (f <= info->bestdist)
498                         Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[1]);
499         }
500         else
501         {
502                 if (((mleaf_t *)node)->numleafsurfaces)
503                         Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
504         }
505 }
506
507 static void Mod_Q1BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, float radius)
508 {
509         int i;
510         findnonsolidlocationinfo_t info;
511         if (model == NULL)
512         {
513                 VectorCopy(in, out);
514                 return;
515         }
516         VectorCopy(in, info.center);
517         info.radius = radius;
518         info.model = model;
519         i = 0;
520         do
521         {
522                 VectorClear(info.nudge);
523                 info.bestdist = radius;
524                 Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode);
525                 VectorAdd(info.center, info.nudge, info.center);
526         }
527         while (info.bestdist < radius && ++i < 10);
528         VectorCopy(info.center, out);
529 }
530
531 int Mod_Q1BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
532 {
533         switch(nativecontents)
534         {
535                 case CONTENTS_EMPTY:
536                         return 0;
537                 case CONTENTS_SOLID:
538                         return SUPERCONTENTS_SOLID;
539                 case CONTENTS_WATER:
540                         return SUPERCONTENTS_WATER;
541                 case CONTENTS_SLIME:
542                         return SUPERCONTENTS_SLIME;
543                 case CONTENTS_LAVA:
544                         return SUPERCONTENTS_LAVA;
545                 case CONTENTS_SKY:
546                         return SUPERCONTENTS_SKY;
547         }
548         return 0;
549 }
550
551 int Mod_Q1BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
552 {
553         if (supercontents & SUPERCONTENTS_SOLID)
554                 return CONTENTS_SOLID;
555         if (supercontents & SUPERCONTENTS_SKY)
556                 return CONTENTS_SKY;
557         if (supercontents & SUPERCONTENTS_LAVA)
558                 return CONTENTS_LAVA;
559         if (supercontents & SUPERCONTENTS_SLIME)
560                 return CONTENTS_SLIME;
561         if (supercontents & SUPERCONTENTS_WATER)
562                 return CONTENTS_WATER;
563         return CONTENTS_EMPTY;
564 }
565
566 typedef struct RecursiveHullCheckTraceInfo_s
567 {
568         // the hull we're tracing through
569         const hull_t *hull;
570
571         // the trace structure to fill in
572         trace_t *trace;
573
574         // start, end, and end - start (in model space)
575         double start[3];
576         double end[3];
577         double dist[3];
578 }
579 RecursiveHullCheckTraceInfo_t;
580
581 // 1/32 epsilon to keep floating point happy
582 #define DIST_EPSILON (0.03125)
583
584 #define HULLCHECKSTATE_EMPTY 0
585 #define HULLCHECKSTATE_SOLID 1
586 #define HULLCHECKSTATE_DONE 2
587
588 static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int num, double p1f, double p2f, double p1[3], double p2[3])
589 {
590         // status variables, these don't need to be saved on the stack when
591         // recursing...  but are because this should be thread-safe
592         // (note: tracing against a bbox is not thread-safe, yet)
593         int ret;
594         mplane_t *plane;
595         double t1, t2;
596
597         // variables that need to be stored on the stack when recursing
598         dclipnode_t *node;
599         int side;
600         double midf, mid[3];
601
602         // LordHavoc: a goto!  everyone flee in terror... :)
603 loc0:
604         // check for empty
605         if (num < 0)
606         {
607                 num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
608                 if (!t->trace->startfound)
609                 {
610                         t->trace->startfound = true;
611                         t->trace->startsupercontents |= num;
612                 }
613                 if (num & SUPERCONTENTS_LIQUIDSMASK)
614                         t->trace->inwater = true;
615                 if (num == 0)
616                         t->trace->inopen = true;
617                 if (num & t->trace->hitsupercontentsmask)
618                 {
619                         // if the first leaf is solid, set startsolid
620                         if (t->trace->allsolid)
621                                 t->trace->startsolid = true;
622 #if COLLISIONPARANOID >= 3
623                         Con_Print("S");
624 #endif
625                         return HULLCHECKSTATE_SOLID;
626                 }
627                 else
628                 {
629                         t->trace->allsolid = false;
630 #if COLLISIONPARANOID >= 3
631                         Con_Print("E");
632 #endif
633                         return HULLCHECKSTATE_EMPTY;
634                 }
635         }
636
637         // find the point distances
638         node = t->hull->clipnodes + num;
639
640         plane = t->hull->planes + node->planenum;
641         if (plane->type < 3)
642         {
643                 t1 = p1[plane->type] - plane->dist;
644                 t2 = p2[plane->type] - plane->dist;
645         }
646         else
647         {
648                 t1 = DotProduct (plane->normal, p1) - plane->dist;
649                 t2 = DotProduct (plane->normal, p2) - plane->dist;
650         }
651
652         if (t1 < 0)
653         {
654                 if (t2 < 0)
655                 {
656 #if COLLISIONPARANOID >= 3
657                         Con_Print("<");
658 #endif
659                         num = node->children[1];
660                         goto loc0;
661                 }
662                 side = 1;
663         }
664         else
665         {
666                 if (t2 >= 0)
667                 {
668 #if COLLISIONPARANOID >= 3
669                         Con_Print(">");
670 #endif
671                         num = node->children[0];
672                         goto loc0;
673                 }
674                 side = 0;
675         }
676
677         // the line intersects, find intersection point
678         // LordHavoc: this uses the original trace for maximum accuracy
679 #if COLLISIONPARANOID >= 3
680         Con_Print("M");
681 #endif
682         if (plane->type < 3)
683         {
684                 t1 = t->start[plane->type] - plane->dist;
685                 t2 = t->end[plane->type] - plane->dist;
686         }
687         else
688         {
689                 t1 = DotProduct (plane->normal, t->start) - plane->dist;
690                 t2 = DotProduct (plane->normal, t->end) - plane->dist;
691         }
692
693         midf = t1 / (t1 - t2);
694         midf = bound(p1f, midf, p2f);
695         VectorMA(t->start, midf, t->dist, mid);
696
697         // recurse both sides, front side first
698         ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side], p1f, midf, p1, mid);
699         // if this side is not empty, return what it is (solid or done)
700         if (ret != HULLCHECKSTATE_EMPTY)
701                 return ret;
702
703         ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side ^ 1], midf, p2f, mid, p2);
704         // if other side is not solid, return what it is (empty or done)
705         if (ret != HULLCHECKSTATE_SOLID)
706                 return ret;
707
708         // front is air and back is solid, this is the impact point...
709         if (side)
710         {
711                 t->trace->plane.dist = -plane->dist;
712                 VectorNegate (plane->normal, t->trace->plane.normal);
713         }
714         else
715         {
716                 t->trace->plane.dist = plane->dist;
717                 VectorCopy (plane->normal, t->trace->plane.normal);
718         }
719
720         // calculate the true fraction
721         t1 = DotProduct(t->trace->plane.normal, t->start) - t->trace->plane.dist;
722         t2 = DotProduct(t->trace->plane.normal, t->end) - t->trace->plane.dist;
723         midf = t1 / (t1 - t2);
724         t->trace->realfraction = bound(0, midf, 1);
725
726         // calculate the return fraction which is nudged off the surface a bit
727         midf = (t1 - DIST_EPSILON) / (t1 - t2);
728         t->trace->fraction = bound(0, midf, 1);
729
730 #if COLLISIONPARANOID >= 3
731         Con_Print("D");
732 #endif
733         return HULLCHECKSTATE_DONE;
734 }
735
736 #if COLLISIONPARANOID < 2
737 static int Mod_Q1BSP_RecursiveHullCheckPoint(RecursiveHullCheckTraceInfo_t *t, int num)
738 {
739         while (num >= 0)
740                 num = t->hull->clipnodes[num].children[(t->hull->planes[t->hull->clipnodes[num].planenum].type < 3 ? t->start[t->hull->planes[t->hull->clipnodes[num].planenum].type] : DotProduct(t->hull->planes[t->hull->clipnodes[num].planenum].normal, t->start)) < t->hull->planes[t->hull->clipnodes[num].planenum].dist];
741         num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
742         t->trace->startsupercontents |= num;
743         if (num & SUPERCONTENTS_LIQUIDSMASK)
744                 t->trace->inwater = true;
745         if (num == 0)
746                 t->trace->inopen = true;
747         if (num & t->trace->hitsupercontentsmask)
748         {
749                 t->trace->allsolid = t->trace->startsolid = true;
750                 return HULLCHECKSTATE_SOLID;
751         }
752         else
753         {
754                 t->trace->allsolid = t->trace->startsolid = false;
755                 return HULLCHECKSTATE_EMPTY;
756         }
757 }
758 #endif
759
760 static void Mod_Q1BSP_TraceBox(struct model_s *model, int frame, trace_t *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask)
761 {
762         // this function currently only supports same size start and end
763         double boxsize[3];
764         RecursiveHullCheckTraceInfo_t rhc;
765
766         memset(&rhc, 0, sizeof(rhc));
767         memset(trace, 0, sizeof(trace_t));
768         rhc.trace = trace;
769         rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
770         rhc.trace->fraction = 1;
771         rhc.trace->realfraction = 1;
772         rhc.trace->allsolid = true;
773         VectorSubtract(boxmaxs, boxmins, boxsize);
774         if (boxsize[0] < 3)
775                 rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
776         else if (model->brush.ismcbsp)
777         {
778                 if (boxsize[2] < 48) // pick the nearest of 40 or 56
779                         rhc.hull = &model->brushq1.hulls[2]; // 16x16x40
780                 else
781                         rhc.hull = &model->brushq1.hulls[1]; // 16x16x56
782         }
783         else if (model->brush.ishlbsp)
784         {
785                 // LordHavoc: this has to have a minor tolerance (the .1) because of
786                 // minor float precision errors from the box being transformed around
787                 if (boxsize[0] < 32.1)
788                 {
789                         if (boxsize[2] < 54) // pick the nearest of 36 or 72
790                                 rhc.hull = &model->brushq1.hulls[3]; // 32x32x36
791                         else
792                                 rhc.hull = &model->brushq1.hulls[1]; // 32x32x72
793                 }
794                 else
795                         rhc.hull = &model->brushq1.hulls[2]; // 64x64x64
796         }
797         else
798         {
799                 // LordHavoc: this has to have a minor tolerance (the .1) because of
800                 // minor float precision errors from the box being transformed around
801                 if (boxsize[0] < 32.1)
802                         rhc.hull = &model->brushq1.hulls[1]; // 32x32x56
803                 else
804                         rhc.hull = &model->brushq1.hulls[2]; // 64x64x88
805         }
806         VectorMAMAM(1, start, 1, boxmins, -1, rhc.hull->clip_mins, rhc.start);
807         VectorMAMAM(1, end, 1, boxmins, -1, rhc.hull->clip_mins, rhc.end);
808         VectorSubtract(rhc.end, rhc.start, rhc.dist);
809 #if COLLISIONPARANOID >= 2
810         Con_Printf("t(%f %f %f,%f %f %f,%i %f %f %f)", rhc.start[0], rhc.start[1], rhc.start[2], rhc.end[0], rhc.end[1], rhc.end[2], rhc.hull - model->brushq1.hulls, rhc.hull->clip_mins[0], rhc.hull->clip_mins[1], rhc.hull->clip_mins[2]);
811         Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
812         Con_Print("\n");
813 #else
814         if (VectorLength2(rhc.dist))
815                 Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
816         else
817                 Mod_Q1BSP_RecursiveHullCheckPoint(&rhc, rhc.hull->firstclipnode);
818 #endif
819 }
820
821 void Collision_ClipTrace_Box(trace_t *trace, const vec3_t cmins, const vec3_t cmaxs, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int hitsupercontentsmask, int boxsupercontents)
822 {
823 #if 1
824         colbrushf_t cbox;
825         colplanef_t cbox_planes[6];
826         cbox.supercontents = boxsupercontents;
827         cbox.numplanes = 6;
828         cbox.numpoints = 0;
829         cbox.numtriangles = 0;
830         cbox.planes = cbox_planes;
831         cbox.points = NULL;
832         cbox.elements = NULL;
833         cbox.markframe = 0;
834         cbox.mins[0] = 0;
835         cbox.mins[1] = 0;
836         cbox.mins[2] = 0;
837         cbox.maxs[0] = 0;
838         cbox.maxs[1] = 0;
839         cbox.maxs[2] = 0;
840         cbox_planes[0].normal[0] =  1;cbox_planes[0].normal[1] =  0;cbox_planes[0].normal[2] =  0;cbox_planes[0].dist = cmaxs[0] - mins[0];
841         cbox_planes[1].normal[0] = -1;cbox_planes[1].normal[1] =  0;cbox_planes[1].normal[2] =  0;cbox_planes[1].dist = maxs[0] - cmins[0];
842         cbox_planes[2].normal[0] =  0;cbox_planes[2].normal[1] =  1;cbox_planes[2].normal[2] =  0;cbox_planes[2].dist = cmaxs[1] - mins[1];
843         cbox_planes[3].normal[0] =  0;cbox_planes[3].normal[1] = -1;cbox_planes[3].normal[2] =  0;cbox_planes[3].dist = maxs[1] - cmins[1];
844         cbox_planes[4].normal[0] =  0;cbox_planes[4].normal[1] =  0;cbox_planes[4].normal[2] =  1;cbox_planes[4].dist = cmaxs[2] - mins[2];
845         cbox_planes[5].normal[0] =  0;cbox_planes[5].normal[1] =  0;cbox_planes[5].normal[2] = -1;cbox_planes[5].dist = maxs[2] - cmins[2];
846         memset(trace, 0, sizeof(trace_t));
847         trace->hitsupercontentsmask = hitsupercontentsmask;
848         trace->fraction = 1;
849         trace->realfraction = 1;
850         Collision_TraceLineBrushFloat(trace, start, end, &cbox, &cbox);
851 #else
852         RecursiveHullCheckTraceInfo_t rhc;
853         static hull_t box_hull;
854         static dclipnode_t box_clipnodes[6];
855         static mplane_t box_planes[6];
856         // fill in a default trace
857         memset(&rhc, 0, sizeof(rhc));
858         memset(trace, 0, sizeof(trace_t));
859         //To keep everything totally uniform, bounding boxes are turned into small
860         //BSP trees instead of being compared directly.
861         // create a temp hull from bounding box sizes
862         box_planes[0].dist = cmaxs[0] - mins[0];
863         box_planes[1].dist = cmins[0] - maxs[0];
864         box_planes[2].dist = cmaxs[1] - mins[1];
865         box_planes[3].dist = cmins[1] - maxs[1];
866         box_planes[4].dist = cmaxs[2] - mins[2];
867         box_planes[5].dist = cmins[2] - maxs[2];
868 #if COLLISIONPARANOID >= 3
869         Con_Printf("box_planes %f:%f %f:%f %f:%f\ncbox %f %f %f:%f %f %f\nbox %f %f %f:%f %f %f\n", box_planes[0].dist, box_planes[1].dist, box_planes[2].dist, box_planes[3].dist, box_planes[4].dist, box_planes[5].dist, cmins[0], cmins[1], cmins[2], cmaxs[0], cmaxs[1], cmaxs[2], mins[0], mins[1], mins[2], maxs[0], maxs[1], maxs[2]);
870 #endif
871
872         if (box_hull.clipnodes == NULL)
873         {
874                 int i, side;
875
876                 //Set up the planes and clipnodes so that the six floats of a bounding box
877                 //can just be stored out and get a proper hull_t structure.
878
879                 box_hull.clipnodes = box_clipnodes;
880                 box_hull.planes = box_planes;
881                 box_hull.firstclipnode = 0;
882                 box_hull.lastclipnode = 5;
883
884                 for (i = 0;i < 6;i++)
885                 {
886                         box_clipnodes[i].planenum = i;
887
888                         side = i&1;
889
890                         box_clipnodes[i].children[side] = CONTENTS_EMPTY;
891                         if (i != 5)
892                                 box_clipnodes[i].children[side^1] = i + 1;
893                         else
894                                 box_clipnodes[i].children[side^1] = CONTENTS_SOLID;
895
896                         box_planes[i].type = i>>1;
897                         box_planes[i].normal[i>>1] = 1;
898                 }
899         }
900
901         // trace a line through the generated clipping hull
902         //rhc.boxsupercontents = boxsupercontents;
903         rhc.hull = &box_hull;
904         rhc.trace = trace;
905         rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
906         rhc.trace->fraction = 1;
907         rhc.trace->realfraction = 1;
908         rhc.trace->allsolid = true;
909         VectorCopy(start, rhc.start);
910         VectorCopy(end, rhc.end);
911         VectorSubtract(rhc.end, rhc.start, rhc.dist);
912         Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
913         //VectorMA(rhc.start, rhc.trace->fraction, rhc.dist, rhc.trace->endpos);
914         if (rhc.trace->startsupercontents)
915                 rhc.trace->startsupercontents = boxsupercontents;
916 #endif
917 }
918
919 static int Mod_Q1BSP_LightPoint_RecursiveBSPNode(model_t *model, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal, const mnode_t *node, float x, float y, float startz, float endz)
920 {
921         int side, distz = endz - startz;
922         float front, back;
923         float mid;
924
925 loc0:
926         if (!node->plane)
927                 return false;           // didn't hit anything
928
929         switch (node->plane->type)
930         {
931         case PLANE_X:
932                 node = node->children[x < node->plane->dist];
933                 goto loc0;
934         case PLANE_Y:
935                 node = node->children[y < node->plane->dist];
936                 goto loc0;
937         case PLANE_Z:
938                 side = startz < node->plane->dist;
939                 if ((endz < node->plane->dist) == side)
940                 {
941                         node = node->children[side];
942                         goto loc0;
943                 }
944                 // found an intersection
945                 mid = node->plane->dist;
946                 break;
947         default:
948                 back = front = x * node->plane->normal[0] + y * node->plane->normal[1];
949                 front += startz * node->plane->normal[2];
950                 back += endz * node->plane->normal[2];
951                 side = front < node->plane->dist;
952                 if ((back < node->plane->dist) == side)
953                 {
954                         node = node->children[side];
955                         goto loc0;
956                 }
957                 // found an intersection
958                 mid = startz + distz * (front - node->plane->dist) / (front - back);
959                 break;
960         }
961
962         // go down front side
963         if (node->children[side]->plane && Mod_Q1BSP_LightPoint_RecursiveBSPNode(model, ambientcolor, diffusecolor, diffusenormal, node->children[side], x, y, startz, mid))
964                 return true;    // hit something
965         else
966         {
967                 // check for impact on this node
968                 if (node->numsurfaces)
969                 {
970                         int i, ds, dt;
971                         msurface_t *surface;
972
973                         surface = model->data_surfaces + node->firstsurface;
974                         for (i = 0;i < node->numsurfaces;i++, surface++)
975                         {
976                                 if (!(surface->texture->basematerialflags & MATERIALFLAG_WALL) || !surface->lightmapinfo->samples)
977                                         continue;       // no lightmaps
978
979                                 ds = (int) (x * surface->lightmapinfo->texinfo->vecs[0][0] + y * surface->lightmapinfo->texinfo->vecs[0][1] + mid * surface->lightmapinfo->texinfo->vecs[0][2] + surface->lightmapinfo->texinfo->vecs[0][3]) - surface->lightmapinfo->texturemins[0];
980                                 dt = (int) (x * surface->lightmapinfo->texinfo->vecs[1][0] + y * surface->lightmapinfo->texinfo->vecs[1][1] + mid * surface->lightmapinfo->texinfo->vecs[1][2] + surface->lightmapinfo->texinfo->vecs[1][3]) - surface->lightmapinfo->texturemins[1];
981
982                                 if (ds >= 0 && ds < surface->lightmapinfo->extents[0] && dt >= 0 && dt < surface->lightmapinfo->extents[1])
983                                 {
984                                         unsigned char *lightmap;
985                                         int lmwidth, lmheight, maps, line3, size3, dsfrac = ds & 15, dtfrac = dt & 15, scale = 0, r00 = 0, g00 = 0, b00 = 0, r01 = 0, g01 = 0, b01 = 0, r10 = 0, g10 = 0, b10 = 0, r11 = 0, g11 = 0, b11 = 0;
986                                         lmwidth = ((surface->lightmapinfo->extents[0]>>4)+1);
987                                         lmheight = ((surface->lightmapinfo->extents[1]>>4)+1);
988                                         line3 = lmwidth * 3; // LordHavoc: *3 for colored lighting
989                                         size3 = lmwidth * lmheight * 3; // LordHavoc: *3 for colored lighting
990
991                                         lightmap = surface->lightmapinfo->samples + ((dt>>4) * lmwidth + (ds>>4))*3; // LordHavoc: *3 for colored lighting
992
993                                         for (maps = 0;maps < MAXLIGHTMAPS && surface->lightmapinfo->styles[maps] != 255;maps++)
994                                         {
995                                                 scale = r_refdef.lightstylevalue[surface->lightmapinfo->styles[maps]];
996                                                 r00 += lightmap[      0] * scale;g00 += lightmap[      1] * scale;b00 += lightmap[      2] * scale;
997                                                 r01 += lightmap[      3] * scale;g01 += lightmap[      4] * scale;b01 += lightmap[      5] * scale;
998                                                 r10 += lightmap[line3+0] * scale;g10 += lightmap[line3+1] * scale;b10 += lightmap[line3+2] * scale;
999                                                 r11 += lightmap[line3+3] * scale;g11 += lightmap[line3+4] * scale;b11 += lightmap[line3+5] * scale;
1000                                                 lightmap += size3;
1001                                         }
1002
1003 /*
1004 LordHavoc: here's the readable version of the interpolation
1005 code, not quite as easy for the compiler to optimize...
1006
1007 dsfrac is the X position in the lightmap pixel, * 16
1008 dtfrac is the Y position in the lightmap pixel, * 16
1009 r00 is top left corner, r01 is top right corner
1010 r10 is bottom left corner, r11 is bottom right corner
1011 g and b are the same layout.
1012 r0 and r1 are the top and bottom intermediate results
1013
1014 first we interpolate the top two points, to get the top
1015 edge sample
1016
1017         r0 = (((r01-r00) * dsfrac) >> 4) + r00;
1018         g0 = (((g01-g00) * dsfrac) >> 4) + g00;
1019         b0 = (((b01-b00) * dsfrac) >> 4) + b00;
1020
1021 then we interpolate the bottom two points, to get the
1022 bottom edge sample
1023
1024         r1 = (((r11-r10) * dsfrac) >> 4) + r10;
1025         g1 = (((g11-g10) * dsfrac) >> 4) + g10;
1026         b1 = (((b11-b10) * dsfrac) >> 4) + b10;
1027
1028 then we interpolate the top and bottom samples to get the
1029 middle sample (the one which was requested)
1030
1031         r = (((r1-r0) * dtfrac) >> 4) + r0;
1032         g = (((g1-g0) * dtfrac) >> 4) + g0;
1033         b = (((b1-b0) * dtfrac) >> 4) + b0;
1034 */
1035
1036                                         ambientcolor[0] += (float) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00)) * (1.0f / 32768.0f);
1037                                         ambientcolor[1] += (float) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00)) * (1.0f / 32768.0f);
1038                                         ambientcolor[2] += (float) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00)) * (1.0f / 32768.0f);
1039                                         return true; // success
1040                                 }
1041                         }
1042                 }
1043
1044                 // go down back side
1045                 node = node->children[side ^ 1];
1046                 startz = mid;
1047                 distz = endz - startz;
1048                 goto loc0;
1049         }
1050 }
1051
1052 void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
1053 {
1054         Mod_Q1BSP_LightPoint_RecursiveBSPNode(model, ambientcolor, diffusecolor, diffusenormal, model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode, p[0], p[1], p[2], p[2] - 65536);
1055         VectorSet(diffusenormal, 0, 0, -1);
1056 }
1057
1058 static void Mod_Q1BSP_DecompressVis(const unsigned char *in, const unsigned char *inend, unsigned char *out, unsigned char *outend)
1059 {
1060         int c;
1061         unsigned char *outstart = out;
1062         while (out < outend)
1063         {
1064                 if (in == inend)
1065                 {
1066                         Con_Printf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
1067                         return;
1068                 }
1069                 c = *in++;
1070                 if (c)
1071                         *out++ = c;
1072                 else
1073                 {
1074                         if (in == inend)
1075                         {
1076                                 Con_Printf("Mod_Q1BSP_DecompressVis: input underrun (during zero-run) on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
1077                                 return;
1078                         }
1079                         for (c = *in++;c > 0;c--)
1080                         {
1081                                 if (out == outend)
1082                                 {
1083                                         Con_Printf("Mod_Q1BSP_DecompressVis: output overrun on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
1084                                         return;
1085                                 }
1086                                 *out++ = 0;
1087                         }
1088                 }
1089         }
1090 }
1091
1092 /*
1093 =============
1094 R_Q1BSP_LoadSplitSky
1095
1096 A sky texture is 256*128, with the right side being a masked overlay
1097 ==============
1098 */
1099 void R_Q1BSP_LoadSplitSky (unsigned char *src, int width, int height, int bytesperpixel)
1100 {
1101         int i, j;
1102         unsigned solidpixels[128*128], alphapixels[128*128];
1103
1104         // if sky isn't the right size, just use it as a solid layer
1105         if (width != 256 || height != 128)
1106         {
1107                 loadmodel->brush.solidskytexture = R_LoadTexture2D(loadmodel->texturepool, "sky_solidtexture", width, height, src, bytesperpixel == 4 ? TEXTYPE_RGBA : TEXTYPE_PALETTE, TEXF_PRECACHE, bytesperpixel == 1 ? palette_complete : NULL);
1108                 loadmodel->brush.alphaskytexture = NULL;
1109                 return;
1110         }
1111
1112         if (bytesperpixel == 4)
1113         {
1114                 for (i = 0;i < 128;i++)
1115                 {
1116                         for (j = 0;j < 128;j++)
1117                         {
1118                                 solidpixels[(i*128) + j] = ((unsigned *)src)[i*256+j+128];
1119                                 alphapixels[(i*128) + j] = ((unsigned *)src)[i*256+j];
1120                         }
1121                 }
1122         }
1123         else
1124         {
1125                 // make an average value for the back to avoid
1126                 // a fringe on the top level
1127                 int p, r, g, b;
1128                 union
1129                 {
1130                         unsigned int i;
1131                         unsigned char b[4];
1132                 }
1133                 rgba;
1134                 r = g = b = 0;
1135                 for (i = 0;i < 128;i++)
1136                 {
1137                         for (j = 0;j < 128;j++)
1138                         {
1139                                 rgba.i = palette_complete[src[i*256 + j + 128]];
1140                                 r += rgba.b[0];
1141                                 g += rgba.b[1];
1142                                 b += rgba.b[2];
1143                         }
1144                 }
1145                 rgba.b[0] = r/(128*128);
1146                 rgba.b[1] = g/(128*128);
1147                 rgba.b[2] = b/(128*128);
1148                 rgba.b[3] = 0;
1149                 for (i = 0;i < 128;i++)
1150                 {
1151                         for (j = 0;j < 128;j++)
1152                         {
1153                                 solidpixels[(i*128) + j] = palette_complete[src[i*256 + j + 128]];
1154                                 alphapixels[(i*128) + j] = (p = src[i*256 + j]) ? palette_complete[p] : rgba.i;
1155                         }
1156                 }
1157         }
1158
1159         loadmodel->brush.solidskytexture = R_LoadTexture2D(loadmodel->texturepool, "sky_solidtexture", 128, 128, (unsigned char *) solidpixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1160         loadmodel->brush.alphaskytexture = R_LoadTexture2D(loadmodel->texturepool, "sky_alphatexture", 128, 128, (unsigned char *) alphapixels, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1161 }
1162
1163 static void Mod_Q1BSP_LoadTextures(lump_t *l)
1164 {
1165         int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
1166         miptex_t *dmiptex;
1167         texture_t *tx, *tx2, *anims[10], *altanims[10];
1168         dmiptexlump_t *m;
1169         unsigned char *data, *mtdata;
1170         char name[MAX_QPATH];
1171
1172         loadmodel->data_textures = NULL;
1173
1174         // add two slots for notexture walls and notexture liquids
1175         if (l->filelen)
1176         {
1177                 m = (dmiptexlump_t *)(mod_base + l->fileofs);
1178                 m->nummiptex = LittleLong (m->nummiptex);
1179                 loadmodel->num_textures = m->nummiptex + 2;
1180         }
1181         else
1182         {
1183                 m = NULL;
1184                 loadmodel->num_textures = 2;
1185         }
1186
1187         loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_textures * sizeof(texture_t));
1188
1189         // fill out all slots with notexture
1190         for (i = 0, tx = loadmodel->data_textures;i < loadmodel->num_textures;i++, tx++)
1191         {
1192                 strcpy(tx->name, "NO TEXTURE FOUND");
1193                 tx->width = 16;
1194                 tx->height = 16;
1195                 tx->skin.base = r_texture_notexture;
1196                 tx->basematerialflags = 0;
1197                 if (i == loadmodel->num_textures - 1)
1198                 {
1199                         tx->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_LIGHTBOTHSIDES;
1200                         tx->supercontents = SUPERCONTENTS_WATER;
1201                 }
1202                 else
1203                 {
1204                         tx->basematerialflags |= MATERIALFLAG_WALL;
1205                         tx->supercontents = SUPERCONTENTS_SOLID;
1206                 }
1207                 tx->currentframe = tx;
1208         }
1209
1210         if (!m)
1211                 return;
1212
1213         // just to work around bounds checking when debugging with it (array index out of bounds error thing)
1214         dofs = m->dataofs;
1215         // LordHavoc: mostly rewritten map texture loader
1216         for (i = 0;i < m->nummiptex;i++)
1217         {
1218                 dofs[i] = LittleLong(dofs[i]);
1219                 if (dofs[i] == -1 || r_nosurftextures.integer)
1220                         continue;
1221                 dmiptex = (miptex_t *)((unsigned char *)m + dofs[i]);
1222
1223                 // make sure name is no more than 15 characters
1224                 for (j = 0;dmiptex->name[j] && j < 15;j++)
1225                         name[j] = dmiptex->name[j];
1226                 name[j] = 0;
1227
1228                 mtwidth = LittleLong(dmiptex->width);
1229                 mtheight = LittleLong(dmiptex->height);
1230                 mtdata = NULL;
1231                 j = LittleLong(dmiptex->offsets[0]);
1232                 if (j)
1233                 {
1234                         // texture included
1235                         if (j < 40 || j + mtwidth * mtheight > l->filelen)
1236                         {
1237                                 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
1238                                 continue;
1239                         }
1240                         mtdata = (unsigned char *)dmiptex + j;
1241                 }
1242
1243                 if ((mtwidth & 15) || (mtheight & 15))
1244                         Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned\n", dmiptex->name, loadmodel->name);
1245
1246                 // LordHavoc: force all names to lowercase
1247                 for (j = 0;name[j];j++)
1248                         if (name[j] >= 'A' && name[j] <= 'Z')
1249                                 name[j] += 'a' - 'A';
1250
1251                 tx = loadmodel->data_textures + i;
1252                 strcpy(tx->name, name);
1253                 tx->width = mtwidth;
1254                 tx->height = mtheight;
1255
1256                 if (!tx->name[0])
1257                 {
1258                         sprintf(tx->name, "unnamed%i", i);
1259                         Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
1260                 }
1261
1262                 if (cls.state != ca_dedicated)
1263                 {
1264                         // LordHavoc: HL sky textures are entirely different than quake
1265                         if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
1266                         {
1267                                 if (loadmodel->isworldmodel)
1268                                 {
1269                                         data = loadimagepixels(tx->name, false, 0, 0);
1270                                         if (data)
1271                                         {
1272                                                 R_Q1BSP_LoadSplitSky(data, image_width, image_height, 4);
1273                                                 Mem_Free(data);
1274                                         }
1275                                         else if (mtdata != NULL)
1276                                                 R_Q1BSP_LoadSplitSky(mtdata, mtwidth, mtheight, 1);
1277                                 }
1278                         }
1279                         else
1280                         {
1281                                 if (!Mod_LoadSkinFrame(&tx->skin, gamemode == GAME_TENEBRAE ? tx->name : va("textures/%s", tx->name), TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, true))
1282                                 {
1283                                         // did not find external texture, load it from the bsp or wad3
1284                                         if (loadmodel->brush.ishlbsp)
1285                                         {
1286                                                 // internal texture overrides wad
1287                                                 unsigned char *pixels, *freepixels;
1288                                                 pixels = freepixels = NULL;
1289                                                 if (mtdata)
1290                                                         pixels = W_ConvertWAD3Texture(dmiptex);
1291                                                 if (pixels == NULL)
1292                                                         pixels = freepixels = W_GetTexture(tx->name);
1293                                                 if (pixels != NULL)
1294                                                 {
1295                                                         tx->width = image_width;
1296                                                         tx->height = image_height;
1297                                                         Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, false, pixels, image_width, image_height, 32, NULL, NULL);
1298                                                 }
1299                                                 if (freepixels)
1300                                                         Mem_Free(freepixels);
1301                                         }
1302                                         else if (mtdata) // texture included
1303                                                 Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE | TEXF_PICMIP, false, r_fullbrights.integer, mtdata, tx->width, tx->height, 8, NULL, NULL);
1304                                 }
1305                         }
1306                         if (tx->skin.base == NULL)
1307                         {
1308                                 // no texture found
1309                                 tx->width = 16;
1310                                 tx->height = 16;
1311                                 tx->skin.base = r_texture_notexture;
1312                         }
1313                 }
1314
1315                 tx->basematerialflags = 0;
1316                 if (tx->name[0] == '*')
1317                 {
1318                         // turb does not block movement
1319                         tx->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_LIGHTBOTHSIDES;
1320                         // LordHavoc: some turbulent textures should not be affected by wateralpha
1321                         if (strncmp(tx->name,"*lava",5)
1322                          && strncmp(tx->name,"*teleport",9)
1323                          && strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
1324                                 tx->basematerialflags |= MATERIALFLAG_WATERALPHA;
1325                         if (!strncmp(tx->name, "*lava", 5))
1326                                 tx->supercontents = SUPERCONTENTS_LAVA;
1327                         else if (!strncmp(tx->name, "*slime", 6))
1328                                 tx->supercontents = SUPERCONTENTS_SLIME;
1329                         else
1330                                 tx->supercontents = SUPERCONTENTS_WATER;
1331                 }
1332                 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
1333                 {
1334                         tx->supercontents = SUPERCONTENTS_SKY;
1335                         tx->basematerialflags |= MATERIALFLAG_SKY;
1336                 }
1337                 else
1338                 {
1339                         tx->supercontents = SUPERCONTENTS_SOLID;
1340                         tx->basematerialflags |= MATERIALFLAG_WALL;
1341                 }
1342                 if (tx->skin.fog)
1343                         tx->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_TRANSPARENT;
1344
1345                 // start out with no animation
1346                 tx->currentframe = tx;
1347         }
1348
1349         // sequence the animations
1350         for (i = 0;i < m->nummiptex;i++)
1351         {
1352                 tx = loadmodel->data_textures + i;
1353                 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
1354                         continue;
1355                 if (tx->anim_total[0] || tx->anim_total[1])
1356                         continue;       // already sequenced
1357
1358                 // find the number of frames in the animation
1359                 memset(anims, 0, sizeof(anims));
1360                 memset(altanims, 0, sizeof(altanims));
1361
1362                 for (j = i;j < m->nummiptex;j++)
1363                 {
1364                         tx2 = loadmodel->data_textures + j;
1365                         if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
1366                                 continue;
1367
1368                         num = tx2->name[1];
1369                         if (num >= '0' && num <= '9')
1370                                 anims[num - '0'] = tx2;
1371                         else if (num >= 'a' && num <= 'j')
1372                                 altanims[num - 'a'] = tx2;
1373                         else
1374                                 Con_Printf("Bad animating texture %s\n", tx->name);
1375                 }
1376
1377                 max = altmax = 0;
1378                 for (j = 0;j < 10;j++)
1379                 {
1380                         if (anims[j])
1381                                 max = j + 1;
1382                         if (altanims[j])
1383                                 altmax = j + 1;
1384                 }
1385                 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
1386
1387                 incomplete = false;
1388                 for (j = 0;j < max;j++)
1389                 {
1390                         if (!anims[j])
1391                         {
1392                                 Con_Printf("Missing frame %i of %s\n", j, tx->name);
1393                                 incomplete = true;
1394                         }
1395                 }
1396                 for (j = 0;j < altmax;j++)
1397                 {
1398                         if (!altanims[j])
1399                         {
1400                                 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
1401                                 incomplete = true;
1402                         }
1403                 }
1404                 if (incomplete)
1405                         continue;
1406
1407                 if (altmax < 1)
1408                 {
1409                         // if there is no alternate animation, duplicate the primary
1410                         // animation into the alternate
1411                         altmax = max;
1412                         for (k = 0;k < 10;k++)
1413                                 altanims[k] = anims[k];
1414                 }
1415
1416                 // link together the primary animation
1417                 for (j = 0;j < max;j++)
1418                 {
1419                         tx2 = anims[j];
1420                         tx2->animated = true;
1421                         tx2->anim_total[0] = max;
1422                         tx2->anim_total[1] = altmax;
1423                         for (k = 0;k < 10;k++)
1424                         {
1425                                 tx2->anim_frames[0][k] = anims[k];
1426                                 tx2->anim_frames[1][k] = altanims[k];
1427                         }
1428                 }
1429
1430                 // if there really is an alternate anim...
1431                 if (anims[0] != altanims[0])
1432                 {
1433                         // link together the alternate animation
1434                         for (j = 0;j < altmax;j++)
1435                         {
1436                                 tx2 = altanims[j];
1437                                 tx2->animated = true;
1438                                 // the primary/alternate are reversed here
1439                                 tx2->anim_total[0] = altmax;
1440                                 tx2->anim_total[1] = max;
1441                                 for (k = 0;k < 10;k++)
1442                                 {
1443                                         tx2->anim_frames[0][k] = altanims[k];
1444                                         tx2->anim_frames[1][k] = anims[k];
1445                                 }
1446                         }
1447                 }
1448         }
1449 }
1450
1451 static void Mod_Q1BSP_LoadLighting(lump_t *l)
1452 {
1453         int i;
1454         unsigned char *in, *out, *data, d;
1455         char litfilename[1024];
1456         fs_offset_t filesize;
1457         loadmodel->brushq1.lightdata = NULL;
1458         if (loadmodel->brush.ishlbsp) // LordHavoc: load the colored lighting data straight
1459         {
1460                 loadmodel->brushq1.lightdata = (unsigned char *)Mem_Alloc(loadmodel->mempool, l->filelen);
1461                 for (i=0; i<l->filelen; i++)
1462                         loadmodel->brushq1.lightdata[i] = mod_base[l->fileofs+i] >>= 1;
1463         }
1464         else if (loadmodel->brush.ismcbsp)
1465         {
1466                 loadmodel->brushq1.lightdata = (unsigned char *)Mem_Alloc(loadmodel->mempool, l->filelen);
1467                 memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen);
1468         }
1469         else // LordHavoc: bsp version 29 (normal white lighting)
1470         {
1471                 // LordHavoc: hope is not lost yet, check for a .lit file to load
1472                 strlcpy (litfilename, loadmodel->name, sizeof (litfilename));
1473                 FS_StripExtension (litfilename, litfilename, sizeof (litfilename));
1474                 strlcat (litfilename, ".lit", sizeof (litfilename));
1475                 data = (unsigned char*) FS_LoadFile(litfilename, tempmempool, false, &filesize);
1476                 if (data)
1477                 {
1478                         if (filesize == (fs_offset_t)(8 + l->filelen * 3) && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
1479                         {
1480                                 i = LittleLong(((int *)data)[1]);
1481                                 if (i == 1)
1482                                 {
1483                                         Con_DPrintf("loaded %s\n", litfilename);
1484                                         loadmodel->brushq1.lightdata = (unsigned char *)Mem_Alloc(loadmodel->mempool, filesize - 8);
1485                                         memcpy(loadmodel->brushq1.lightdata, data + 8, filesize - 8);
1486                                         Mem_Free(data);
1487                                         return;
1488                                 }
1489                                 else
1490                                 {
1491                                         Con_Printf("Unknown .lit file version (%d)\n", i);
1492                                         Mem_Free(data);
1493                                 }
1494                         }
1495                         else
1496                         {
1497                                 if (filesize == 8)
1498                                         Con_Print("Empty .lit file, ignoring\n");
1499                                 else
1500                                         Con_Printf("Corrupt .lit file (file size %i bytes, should be %i bytes), ignoring\n", filesize, 8 + l->filelen * 3);
1501                                 Mem_Free(data);
1502                         }
1503                 }
1504                 // LordHavoc: oh well, expand the white lighting data
1505                 if (!l->filelen)
1506                         return;
1507                 loadmodel->brushq1.lightdata = (unsigned char *)Mem_Alloc(loadmodel->mempool, l->filelen*3);
1508                 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
1509                 out = loadmodel->brushq1.lightdata;
1510                 memcpy(in, mod_base + l->fileofs, l->filelen);
1511                 for (i = 0;i < l->filelen;i++)
1512                 {
1513                         d = *in++;
1514                         *out++ = d;
1515                         *out++ = d;
1516                         *out++ = d;
1517                 }
1518         }
1519 }
1520
1521 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1522 {
1523         loadmodel->brushq1.num_compressedpvs = 0;
1524         loadmodel->brushq1.data_compressedpvs = NULL;
1525         if (!l->filelen)
1526                 return;
1527         loadmodel->brushq1.num_compressedpvs = l->filelen;
1528         loadmodel->brushq1.data_compressedpvs = (unsigned char *)Mem_Alloc(loadmodel->mempool, l->filelen);
1529         memcpy(loadmodel->brushq1.data_compressedpvs, mod_base + l->fileofs, l->filelen);
1530 }
1531
1532 // used only for HalfLife maps
1533 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1534 {
1535         char key[128], value[4096];
1536         char wadname[128];
1537         int i, j, k;
1538         if (!data)
1539                 return;
1540         if (!COM_ParseToken(&data, false))
1541                 return; // error
1542         if (com_token[0] != '{')
1543                 return; // error
1544         while (1)
1545         {
1546                 if (!COM_ParseToken(&data, false))
1547                         return; // error
1548                 if (com_token[0] == '}')
1549                         break; // end of worldspawn
1550                 if (com_token[0] == '_')
1551                         strcpy(key, com_token + 1);
1552                 else
1553                         strcpy(key, com_token);
1554                 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1555                         key[strlen(key)-1] = 0;
1556                 if (!COM_ParseToken(&data, false))
1557                         return; // error
1558                 dpsnprintf(value, sizeof(value), "%s", com_token);
1559                 if (!strcmp("wad", key)) // for HalfLife maps
1560                 {
1561                         if (loadmodel->brush.ishlbsp)
1562                         {
1563                                 j = 0;
1564                                 for (i = 0;i < (int)sizeof(value);i++)
1565                                         if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1566                                                 break;
1567                                 if (value[i])
1568                                 {
1569                                         for (;i < (int)sizeof(value);i++)
1570                                         {
1571                                                 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1572                                                 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1573                                                         j = i+1;
1574                                                 else if (value[i] == ';' || value[i] == 0)
1575                                                 {
1576                                                         k = value[i];
1577                                                         value[i] = 0;
1578                                                         strcpy(wadname, "textures/");
1579                                                         strcat(wadname, &value[j]);
1580                                                         W_LoadTextureWadFile(wadname, false);
1581                                                         j = i+1;
1582                                                         if (!k)
1583                                                                 break;
1584                                                 }
1585                                         }
1586                                 }
1587                         }
1588                 }
1589         }
1590 }
1591
1592 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1593 {
1594         loadmodel->brush.entities = NULL;
1595         if (!l->filelen)
1596                 return;
1597         loadmodel->brush.entities = (char *)Mem_Alloc(loadmodel->mempool, l->filelen);
1598         memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1599         if (loadmodel->brush.ishlbsp)
1600                 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1601 }
1602
1603
1604 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1605 {
1606         dvertex_t       *in;
1607         mvertex_t       *out;
1608         int                     i, count;
1609
1610         in = (dvertex_t *)(mod_base + l->fileofs);
1611         if (l->filelen % sizeof(*in))
1612                 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1613         count = l->filelen / sizeof(*in);
1614         out = (mvertex_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1615
1616         loadmodel->brushq1.vertexes = out;
1617         loadmodel->brushq1.numvertexes = count;
1618
1619         for ( i=0 ; i<count ; i++, in++, out++)
1620         {
1621                 out->position[0] = LittleFloat(in->point[0]);
1622                 out->position[1] = LittleFloat(in->point[1]);
1623                 out->position[2] = LittleFloat(in->point[2]);
1624         }
1625 }
1626
1627 // The following two functions should be removed and MSG_* or SZ_* function sets adjusted so they
1628 // can be used for this
1629 // REMOVEME
1630 int SB_ReadInt (unsigned char **buffer)
1631 {
1632         int     i;
1633         i = ((*buffer)[0]) + 256*((*buffer)[1]) + 65536*((*buffer)[2]) + 16777216*((*buffer)[3]);
1634         (*buffer) += 4;
1635         return i;
1636 }
1637
1638 // REMOVEME
1639 float SB_ReadFloat (unsigned char **buffer)
1640 {
1641         union
1642         {
1643                 int             i;
1644                 float   f;
1645         } u;
1646
1647         u.i = SB_ReadInt (buffer);
1648         return u.f;
1649 }
1650
1651 static void Mod_Q1BSP_LoadSubmodels(lump_t *l, hullinfo_t *hullinfo)
1652 {
1653         unsigned char           *index;
1654         dmodel_t        *out;
1655         int                     i, j, count;
1656
1657         index = (unsigned char *)(mod_base + l->fileofs);
1658         if (l->filelen % (48+4*hullinfo->filehulls))
1659                 Host_Error ("Mod_Q1BSP_LoadSubmodels: funny lump size in %s", loadmodel->name);
1660
1661         count = l->filelen / (48+4*hullinfo->filehulls);
1662         out = (dmodel_t *)Mem_Alloc (loadmodel->mempool, count*sizeof(*out));
1663
1664         loadmodel->brushq1.submodels = out;
1665         loadmodel->brush.numsubmodels = count;
1666
1667         for (i = 0; i < count; i++, out++)
1668         {
1669         // spread out the mins / maxs by a pixel
1670                 out->mins[0] = SB_ReadFloat (&index) - 1;
1671                 out->mins[1] = SB_ReadFloat (&index) - 1;
1672                 out->mins[2] = SB_ReadFloat (&index) - 1;
1673                 out->maxs[0] = SB_ReadFloat (&index) + 1;
1674                 out->maxs[1] = SB_ReadFloat (&index) + 1;
1675                 out->maxs[2] = SB_ReadFloat (&index) + 1;
1676                 out->origin[0] = SB_ReadFloat (&index);
1677                 out->origin[1] = SB_ReadFloat (&index);
1678                 out->origin[2] = SB_ReadFloat (&index);
1679                 for (j = 0; j < hullinfo->filehulls; j++)
1680                         out->headnode[j] = SB_ReadInt (&index);
1681                 out->visleafs = SB_ReadInt (&index);
1682                 out->firstface = SB_ReadInt (&index);
1683                 out->numfaces = SB_ReadInt (&index);
1684         }
1685 }
1686
1687 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1688 {
1689         dedge_t *in;
1690         medge_t *out;
1691         int     i, count;
1692
1693         in = (dedge_t *)(mod_base + l->fileofs);
1694         if (l->filelen % sizeof(*in))
1695                 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1696         count = l->filelen / sizeof(*in);
1697         out = (medge_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1698
1699         loadmodel->brushq1.edges = out;
1700         loadmodel->brushq1.numedges = count;
1701
1702         for ( i=0 ; i<count ; i++, in++, out++)
1703         {
1704                 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1705                 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1706                 if (out->v[0] >= loadmodel->brushq1.numvertexes || out->v[1] >= loadmodel->brushq1.numvertexes)
1707                 {
1708                         Con_Printf("Mod_Q1BSP_LoadEdges: %s has invalid vertex indices in edge %i (vertices %i %i >= numvertices %i)\n", loadmodel->name, i, out->v[0], out->v[1], loadmodel->brushq1.numvertexes);
1709                         out->v[0] = 0;
1710                         out->v[1] = 0;
1711                 }
1712         }
1713 }
1714
1715 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1716 {
1717         texinfo_t *in;
1718         mtexinfo_t *out;
1719         int i, j, k, count, miptex;
1720
1721         in = (texinfo_t *)(mod_base + l->fileofs);
1722         if (l->filelen % sizeof(*in))
1723                 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1724         count = l->filelen / sizeof(*in);
1725         out = (mtexinfo_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1726
1727         loadmodel->brushq1.texinfo = out;
1728         loadmodel->brushq1.numtexinfo = count;
1729
1730         for (i = 0;i < count;i++, in++, out++)
1731         {
1732                 for (k = 0;k < 2;k++)
1733                         for (j = 0;j < 4;j++)
1734                                 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1735
1736                 miptex = LittleLong(in->miptex);
1737                 out->flags = LittleLong(in->flags);
1738
1739                 out->texture = NULL;
1740                 if (loadmodel->data_textures)
1741                 {
1742                         if ((unsigned int) miptex >= (unsigned int) loadmodel->num_textures)
1743                                 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->num_textures);
1744                         else
1745                                 out->texture = loadmodel->data_textures + miptex;
1746                 }
1747                 if (out->flags & TEX_SPECIAL)
1748                 {
1749                         // if texture chosen is NULL or the shader needs a lightmap,
1750                         // force to notexture water shader
1751                         if (out->texture == NULL || out->texture->basematerialflags & MATERIALFLAG_WALL)
1752                                 out->texture = loadmodel->data_textures + (loadmodel->num_textures - 1);
1753                 }
1754                 else
1755                 {
1756                         // if texture chosen is NULL, force to notexture
1757                         if (out->texture == NULL)
1758                                 out->texture = loadmodel->data_textures + (loadmodel->num_textures - 2);
1759                 }
1760         }
1761 }
1762
1763 #if 0
1764 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1765 {
1766         int             i, j;
1767         float   *v;
1768
1769         mins[0] = mins[1] = mins[2] = 9999;
1770         maxs[0] = maxs[1] = maxs[2] = -9999;
1771         v = verts;
1772         for (i = 0;i < numverts;i++)
1773         {
1774                 for (j = 0;j < 3;j++, v++)
1775                 {
1776                         if (*v < mins[j])
1777                                 mins[j] = *v;
1778                         if (*v > maxs[j])
1779                                 maxs[j] = *v;
1780                 }
1781         }
1782 }
1783
1784 #define MAX_SUBDIVPOLYTRIANGLES 4096
1785 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1786
1787 static int subdivpolyverts, subdivpolytriangles;
1788 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1789 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1790
1791 static int subdivpolylookupvert(vec3_t v)
1792 {
1793         int i;
1794         for (i = 0;i < subdivpolyverts;i++)
1795                 if (subdivpolyvert[i][0] == v[0]
1796                  && subdivpolyvert[i][1] == v[1]
1797                  && subdivpolyvert[i][2] == v[2])
1798                         return i;
1799         if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1800                 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1801         VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1802         return subdivpolyverts++;
1803 }
1804
1805 static void SubdividePolygon(int numverts, float *verts)
1806 {
1807         int             i, i1, i2, i3, f, b, c, p;
1808         vec3_t  mins, maxs, front[256], back[256];
1809         float   m, *pv, *cv, dist[256], frac;
1810
1811         if (numverts > 250)
1812                 Host_Error("SubdividePolygon: ran out of verts in buffer");
1813
1814         BoundPoly(numverts, verts, mins, maxs);
1815
1816         for (i = 0;i < 3;i++)
1817         {
1818                 m = (mins[i] + maxs[i]) * 0.5;
1819                 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1820                 if (maxs[i] - m < 8)
1821                         continue;
1822                 if (m - mins[i] < 8)
1823                         continue;
1824
1825                 // cut it
1826                 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1827                         dist[c] = cv[i] - m;
1828
1829                 f = b = 0;
1830                 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1831                 {
1832                         if (dist[p] >= 0)
1833                         {
1834                                 VectorCopy(pv, front[f]);
1835                                 f++;
1836                         }
1837                         if (dist[p] <= 0)
1838                         {
1839                                 VectorCopy(pv, back[b]);
1840                                 b++;
1841                         }
1842                         if (dist[p] == 0 || dist[c] == 0)
1843                                 continue;
1844                         if ((dist[p] > 0) != (dist[c] > 0) )
1845                         {
1846                                 // clip point
1847                                 frac = dist[p] / (dist[p] - dist[c]);
1848                                 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1849                                 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1850                                 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1851                                 f++;
1852                                 b++;
1853                         }
1854                 }
1855
1856                 SubdividePolygon(f, front[0]);
1857                 SubdividePolygon(b, back[0]);
1858                 return;
1859         }
1860
1861         i1 = subdivpolylookupvert(verts);
1862         i2 = subdivpolylookupvert(verts + 3);
1863         for (i = 2;i < numverts;i++)
1864         {
1865                 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1866                 {
1867                         Con_Print("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1868                         return;
1869                 }
1870
1871                 i3 = subdivpolylookupvert(verts + i * 3);
1872                 subdivpolyindex[subdivpolytriangles][0] = i1;
1873                 subdivpolyindex[subdivpolytriangles][1] = i2;
1874                 subdivpolyindex[subdivpolytriangles][2] = i3;
1875                 i2 = i3;
1876                 subdivpolytriangles++;
1877         }
1878 }
1879
1880 //Breaks a polygon up along axial 64 unit
1881 //boundaries so that turbulent and sky warps
1882 //can be done reasonably.
1883 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surface)
1884 {
1885         int i, j;
1886         surfvertex_t *v;
1887         surfmesh_t *mesh;
1888
1889         subdivpolytriangles = 0;
1890         subdivpolyverts = 0;
1891         SubdividePolygon(surface->num_vertices, (surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex));
1892         if (subdivpolytriangles < 1)
1893                 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?");
1894
1895         surface->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1896         mesh->num_vertices = subdivpolyverts;
1897         mesh->num_triangles = subdivpolytriangles;
1898         mesh->vertex = (surfvertex_t *)(mesh + 1);
1899         mesh->index = (int *)(mesh->vertex + mesh->num_vertices);
1900         memset(mesh->vertex, 0, mesh->num_vertices * sizeof(surfvertex_t));
1901
1902         for (i = 0;i < mesh->num_triangles;i++)
1903                 for (j = 0;j < 3;j++)
1904                         mesh->index[i*3+j] = subdivpolyindex[i][j];
1905
1906         for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1907         {
1908                 VectorCopy(subdivpolyvert[i], v->v);
1909                 v->st[0] = DotProduct(v->v, surface->lightmapinfo->texinfo->vecs[0]);
1910                 v->st[1] = DotProduct(v->v, surface->lightmapinfo->texinfo->vecs[1]);
1911         }
1912 }
1913 #endif
1914
1915 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1916 {
1917         dface_t *in;
1918         msurface_t *surface;
1919         int i, j, count, surfacenum, planenum, smax, tmax, ssize, tsize, firstedge, numedges, totalverts, totaltris;
1920         float texmins[2], texmaxs[2], val;
1921
1922         in = (dface_t *)(mod_base + l->fileofs);
1923         if (l->filelen % sizeof(*in))
1924                 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1925         count = l->filelen / sizeof(*in);
1926         loadmodel->data_surfaces = (msurface_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1927         loadmodel->data_surfaces_lightmapinfo = (msurface_lightmapinfo_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_lightmapinfo_t));
1928
1929         loadmodel->num_surfaces = count;
1930
1931         totalverts = 0;
1932         totaltris = 0;
1933         for (surfacenum = 0, in = (dface_t *)(mod_base + l->fileofs);surfacenum < count;surfacenum++, in++)
1934         {
1935                 numedges = LittleShort(in->numedges);
1936                 totalverts += numedges;
1937                 totaltris += numedges - 2;
1938         }
1939
1940         // TODO: split up into multiple meshes as needed to avoid exceeding 65536
1941         // vertex limit
1942         loadmodel->nummeshes = 1;
1943         loadmodel->meshlist = (surfmesh_t **)Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t *));
1944         loadmodel->meshlist[0] = Mod_AllocSurfMesh(loadmodel->mempool, totalverts, totaltris, true, false, false);
1945
1946         totalverts = 0;
1947         totaltris = 0;
1948         for (surfacenum = 0, in = (dface_t *)(mod_base + l->fileofs), surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, in++, surface++)
1949         {
1950                 surface->lightmapinfo = loadmodel->data_surfaces_lightmapinfo + surfacenum;
1951
1952                 // FIXME: validate edges, texinfo, etc?
1953                 firstedge = LittleLong(in->firstedge);
1954                 numedges = LittleShort(in->numedges);
1955                 if ((unsigned int) firstedge > (unsigned int) loadmodel->brushq1.numsurfedges || (unsigned int) numedges > (unsigned int) loadmodel->brushq1.numsurfedges || (unsigned int) firstedge + (unsigned int) numedges > (unsigned int) loadmodel->brushq1.numsurfedges)
1956                         Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)", firstedge, numedges, loadmodel->brushq1.numsurfedges);
1957                 i = LittleShort(in->texinfo);
1958                 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
1959                         Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)", i, loadmodel->brushq1.numtexinfo);
1960                 surface->lightmapinfo->texinfo = loadmodel->brushq1.texinfo + i;
1961                 surface->texture = surface->lightmapinfo->texinfo->texture;
1962
1963                 planenum = LittleShort(in->planenum);
1964                 if ((unsigned int) planenum >= (unsigned int) loadmodel->brush.num_planes)
1965                         Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)", planenum, loadmodel->brush.num_planes);
1966
1967                 //surface->flags = surface->texture->flags;
1968                 //if (LittleShort(in->side))
1969                 //      surface->flags |= SURF_PLANEBACK;
1970                 //surface->plane = loadmodel->brush.data_planes + planenum;
1971
1972                 surface->groupmesh = loadmodel->meshlist[0];
1973                 surface->num_firstvertex = totalverts;
1974                 surface->num_vertices = numedges;
1975                 surface->num_firsttriangle = totaltris;
1976                 surface->num_triangles = numedges - 2;
1977                 totalverts += numedges;
1978                 totaltris += numedges - 2;
1979
1980                 // convert edges back to a normal polygon
1981                 for (i = 0;i < surface->num_vertices;i++)
1982                 {
1983                         int lindex = loadmodel->brushq1.surfedges[firstedge + i];
1984                         float s, t;
1985                         if (lindex > 0)
1986                                 VectorCopy(loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position, (surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex) + i * 3);
1987                         else
1988                                 VectorCopy(loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position, (surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex) + i * 3);
1989                         s = DotProduct(((surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex) + i * 3), surface->lightmapinfo->texinfo->vecs[0]) + surface->lightmapinfo->texinfo->vecs[0][3];
1990                         t = DotProduct(((surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex) + i * 3), surface->lightmapinfo->texinfo->vecs[1]) + surface->lightmapinfo->texinfo->vecs[1][3];
1991                         (surface->groupmesh->data_texcoordtexture2f + 2 * surface->num_firstvertex)[i * 2 + 0] = s / surface->texture->width;
1992                         (surface->groupmesh->data_texcoordtexture2f + 2 * surface->num_firstvertex)[i * 2 + 1] = t / surface->texture->height;
1993                         (surface->groupmesh->data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 0] = 0;
1994                         (surface->groupmesh->data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 1] = 0;
1995                         (surface->groupmesh->data_lightmapoffsets + surface->num_firstvertex)[i] = 0;
1996                 }
1997
1998                 for (i = 0;i < surface->num_triangles;i++)
1999                 {
2000                         (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle)[i * 3 + 0] = 0 + surface->num_firstvertex;
2001                         (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle)[i * 3 + 1] = i + 1 + surface->num_firstvertex;
2002                         (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle)[i * 3 + 2] = i + 2 + surface->num_firstvertex;
2003                 }
2004
2005                 // compile additional data about the surface geometry
2006                 Mod_BuildTextureVectorsAndNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, surface->groupmesh->data_vertex3f, surface->groupmesh->data_texcoordtexture2f, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle), surface->groupmesh->data_svector3f, surface->groupmesh->data_tvector3f, surface->groupmesh->data_normal3f, true);
2007                 BoxFromPoints(surface->mins, surface->maxs, surface->num_vertices, (surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex));
2008
2009                 // generate surface extents information
2010                 texmins[0] = texmaxs[0] = DotProduct((surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex), surface->lightmapinfo->texinfo->vecs[0]) + surface->lightmapinfo->texinfo->vecs[0][3];
2011                 texmins[1] = texmaxs[1] = DotProduct((surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex), surface->lightmapinfo->texinfo->vecs[1]) + surface->lightmapinfo->texinfo->vecs[1][3];
2012                 for (i = 1;i < surface->num_vertices;i++)
2013                 {
2014                         for (j = 0;j < 2;j++)
2015                         {
2016                                 val = DotProduct((surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex) + i * 3, surface->lightmapinfo->texinfo->vecs[j]) + surface->lightmapinfo->texinfo->vecs[j][3];
2017                                 texmins[j] = min(texmins[j], val);
2018                                 texmaxs[j] = max(texmaxs[j], val);
2019                         }
2020                 }
2021                 for (i = 0;i < 2;i++)
2022                 {
2023                         surface->lightmapinfo->texturemins[i] = (int) floor(texmins[i] / 16.0) * 16;
2024                         surface->lightmapinfo->extents[i] = (int) ceil(texmaxs[i] / 16.0) * 16 - surface->lightmapinfo->texturemins[i];
2025                 }
2026
2027                 smax = surface->lightmapinfo->extents[0] >> 4;
2028                 tmax = surface->lightmapinfo->extents[1] >> 4;
2029                 ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
2030                 tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
2031
2032                 // lighting info
2033                 for (i = 0;i < MAXLIGHTMAPS;i++)
2034                         surface->lightmapinfo->styles[i] = in->styles[i];
2035                 surface->lightmapinfo->lightmaptexturestride = 0;
2036                 surface->lightmaptexture = NULL;
2037                 surface->deluxemaptexture = r_texture_blanknormalmap;
2038                 i = LittleLong(in->lightofs);
2039                 if (i == -1)
2040                 {
2041                         surface->lightmapinfo->samples = NULL;
2042                         // give non-lightmapped water a 1x white lightmap
2043                         if ((surface->texture->basematerialflags & MATERIALFLAG_WATER) && (surface->lightmapinfo->texinfo->flags & TEX_SPECIAL) && ssize <= 256 && tsize <= 256)
2044                         {
2045                                 surface->lightmapinfo->samples = (unsigned char *)Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
2046                                 surface->lightmapinfo->styles[0] = 0;
2047                                 memset(surface->lightmapinfo->samples, 128, ssize * tsize * 3);
2048                         }
2049                 }
2050                 else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
2051                         surface->lightmapinfo->samples = loadmodel->brushq1.lightdata + i;
2052                 else // LordHavoc: white lighting (bsp version 29)
2053                         surface->lightmapinfo->samples = loadmodel->brushq1.lightdata + (i * 3);
2054
2055                 // check if we should apply a lightmap to this
2056                 if (!(surface->lightmapinfo->texinfo->flags & TEX_SPECIAL) || surface->lightmapinfo->samples)
2057                 {
2058                         int i, iu, iv;
2059                         float u, v, ubase, vbase, uscale, vscale;
2060
2061                         if (ssize > 256 || tsize > 256)
2062                                 Host_Error("Bad surface extents");
2063                         // force lightmap upload on first time seeing the surface
2064                         surface->cached_dlight = true;
2065                         // stainmap for permanent marks on walls
2066                         surface->lightmapinfo->stainsamples = (unsigned char *)Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
2067                         // clear to white
2068                         memset(surface->lightmapinfo->stainsamples, 255, ssize * tsize * 3);
2069
2070                         if (r_miplightmaps.integer)
2071                         {
2072                                 surface->lightmapinfo->lightmaptexturestride = ssize;
2073                                 surface->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surface->lightmapinfo->lightmaptexturestride, tsize, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_MIPMAP | TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
2074                                 surface->deluxemaptexture = r_texture_blanknormalmap;
2075                         }
2076                         else
2077                         {
2078                                 surface->lightmapinfo->lightmaptexturestride = R_CompatibleFragmentWidth(ssize, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
2079                                 surface->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surface->lightmapinfo->lightmaptexturestride, tsize, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FRAGMENT | TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
2080                                 surface->deluxemaptexture = r_texture_blanknormalmap;
2081                         }
2082                         R_FragmentLocation(surface->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
2083                         uscale = (uscale - ubase) / ssize;
2084                         vscale = (vscale - vbase) / tsize;
2085
2086                         for (i = 0;i < surface->num_vertices;i++)
2087                         {
2088                                 u = ((DotProduct(((surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex) + i * 3), surface->lightmapinfo->texinfo->vecs[0]) + surface->lightmapinfo->texinfo->vecs[0][3]) + 8 - surface->lightmapinfo->texturemins[0]) * (1.0 / 16.0);
2089                                 v = ((DotProduct(((surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex) + i * 3), surface->lightmapinfo->texinfo->vecs[1]) + surface->lightmapinfo->texinfo->vecs[1][3]) + 8 - surface->lightmapinfo->texturemins[1]) * (1.0 / 16.0);
2090                                 (surface->groupmesh->data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 0] = u * uscale + ubase;
2091                                 (surface->groupmesh->data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 1] = v * vscale + vbase;
2092                                 // LordHavoc: calc lightmap data offset for vertex lighting to use
2093                                 iu = (int) u;
2094                                 iv = (int) v;
2095                                 (surface->groupmesh->data_lightmapoffsets + surface->num_firstvertex)[i] = (bound(0, iv, tmax) * ssize + bound(0, iu, smax)) * 3;
2096                         }
2097                 }
2098         }
2099 }
2100
2101 static void Mod_Q1BSP_LoadNodes_RecursiveSetParent(mnode_t *node, mnode_t *parent)
2102 {
2103         //if (node->parent)
2104         //      Host_Error("Mod_Q1BSP_LoadNodes_RecursiveSetParent: runaway recursion");
2105         node->parent = parent;
2106         if (node->plane)
2107         {
2108                 Mod_Q1BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
2109                 Mod_Q1BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
2110         }
2111 }
2112
2113 static void Mod_Q1BSP_LoadNodes(lump_t *l)
2114 {
2115         int                     i, j, count, p;
2116         dnode_t         *in;
2117         mnode_t         *out;
2118
2119         in = (dnode_t *)(mod_base + l->fileofs);
2120         if (l->filelen % sizeof(*in))
2121                 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
2122         count = l->filelen / sizeof(*in);
2123         out = (mnode_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2124
2125         loadmodel->brush.data_nodes = out;
2126         loadmodel->brush.num_nodes = count;
2127
2128         for ( i=0 ; i<count ; i++, in++, out++)
2129         {
2130                 for (j=0 ; j<3 ; j++)
2131                 {
2132                         out->mins[j] = LittleShort(in->mins[j]);
2133                         out->maxs[j] = LittleShort(in->maxs[j]);
2134                 }
2135
2136                 p = LittleLong(in->planenum);
2137                 out->plane = loadmodel->brush.data_planes + p;
2138
2139                 out->firstsurface = LittleShort(in->firstface);
2140                 out->numsurfaces = LittleShort(in->numfaces);
2141
2142                 for (j=0 ; j<2 ; j++)
2143                 {
2144                         p = LittleShort(in->children[j]);
2145                         if (p >= 0)
2146                                 out->children[j] = loadmodel->brush.data_nodes + p;
2147                         else
2148                                 out->children[j] = (mnode_t *)(loadmodel->brush.data_leafs + (-1 - p));
2149                 }
2150         }
2151
2152         Mod_Q1BSP_LoadNodes_RecursiveSetParent(loadmodel->brush.data_nodes, NULL);      // sets nodes and leafs
2153 }
2154
2155 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
2156 {
2157         dleaf_t *in;
2158         mleaf_t *out;
2159         int i, j, count, p;
2160
2161         in = (dleaf_t *)(mod_base + l->fileofs);
2162         if (l->filelen % sizeof(*in))
2163                 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
2164         count = l->filelen / sizeof(*in);
2165         out = (mleaf_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2166
2167         loadmodel->brush.data_leafs = out;
2168         loadmodel->brush.num_leafs = count;
2169         // get visleafs from the submodel data
2170         loadmodel->brush.num_pvsclusters = loadmodel->brushq1.submodels[0].visleafs;
2171         loadmodel->brush.num_pvsclusterbytes = (loadmodel->brush.num_pvsclusters+7)>>3;
2172         loadmodel->brush.data_pvsclusters = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_pvsclusters * loadmodel->brush.num_pvsclusterbytes);
2173         memset(loadmodel->brush.data_pvsclusters, 0xFF, loadmodel->brush.num_pvsclusters * loadmodel->brush.num_pvsclusterbytes);
2174
2175         for ( i=0 ; i<count ; i++, in++, out++)
2176         {
2177                 for (j=0 ; j<3 ; j++)
2178                 {
2179                         out->mins[j] = LittleShort(in->mins[j]);
2180                         out->maxs[j] = LittleShort(in->maxs[j]);
2181                 }
2182
2183                 // FIXME: this function could really benefit from some error checking
2184
2185                 out->contents = LittleLong(in->contents);
2186
2187                 out->firstleafsurface = loadmodel->brush.data_leafsurfaces + LittleShort(in->firstmarksurface);
2188                 out->numleafsurfaces = LittleShort(in->nummarksurfaces);
2189                 if (out->firstleafsurface < 0 || LittleShort(in->firstmarksurface) + out->numleafsurfaces > loadmodel->brush.num_leafsurfaces)
2190                 {
2191                         Con_Printf("Mod_Q1BSP_LoadLeafs: invalid leafsurface range %i:%i outside range %i:%i\n", out->firstleafsurface, out->firstleafsurface + out->numleafsurfaces, 0, loadmodel->brush.num_leafsurfaces);
2192                         out->firstleafsurface = NULL;
2193                         out->numleafsurfaces = 0;
2194                 }
2195
2196                 out->clusterindex = i - 1;
2197                 if (out->clusterindex >= loadmodel->brush.num_pvsclusters)
2198                         out->clusterindex = -1;
2199
2200                 p = LittleLong(in->visofs);
2201                 // ignore visofs errors on leaf 0 (solid)
2202                 if (p >= 0 && out->clusterindex >= 0)
2203                 {
2204                         if (p >= loadmodel->brushq1.num_compressedpvs)
2205                                 Con_Print("Mod_Q1BSP_LoadLeafs: invalid visofs\n");
2206                         else
2207                                 Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, loadmodel->brush.data_pvsclusters + out->clusterindex * loadmodel->brush.num_pvsclusterbytes, loadmodel->brush.data_pvsclusters + (out->clusterindex + 1) * loadmodel->brush.num_pvsclusterbytes);
2208                 }
2209
2210                 for (j = 0;j < 4;j++)
2211                         out->ambient_sound_level[j] = in->ambient_level[j];
2212
2213                 // FIXME: Insert caustics here
2214         }
2215 }
2216
2217 static void Mod_Q1BSP_LoadClipnodes(lump_t *l, hullinfo_t *hullinfo)
2218 {
2219         dclipnode_t *in, *out;
2220         int                     i, count;
2221         hull_t          *hull;
2222
2223         in = (dclipnode_t *)(mod_base + l->fileofs);
2224         if (l->filelen % sizeof(*in))
2225                 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
2226         count = l->filelen / sizeof(*in);
2227         out = (dclipnode_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2228
2229         loadmodel->brushq1.clipnodes = out;
2230         loadmodel->brushq1.numclipnodes = count;
2231
2232         for (i = 1; i < hullinfo->numhulls; i++)
2233         {
2234                 hull = &loadmodel->brushq1.hulls[i];
2235                 hull->clipnodes = out;
2236                 hull->firstclipnode = 0;
2237                 hull->lastclipnode = count-1;
2238                 hull->planes = loadmodel->brush.data_planes;
2239                 hull->clip_mins[0] = hullinfo->hullsizes[i][0][0];
2240                 hull->clip_mins[1] = hullinfo->hullsizes[i][0][1];
2241                 hull->clip_mins[2] = hullinfo->hullsizes[i][0][2];
2242                 hull->clip_maxs[0] = hullinfo->hullsizes[i][1][0];
2243                 hull->clip_maxs[1] = hullinfo->hullsizes[i][1][1];
2244                 hull->clip_maxs[2] = hullinfo->hullsizes[i][1][2];
2245                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2246         }
2247
2248         for (i=0 ; i<count ; i++, out++, in++)
2249         {
2250                 out->planenum = LittleLong(in->planenum);
2251                 out->children[0] = LittleShort(in->children[0]);
2252                 out->children[1] = LittleShort(in->children[1]);
2253                 if (out->children[0] >= count || out->children[1] >= count)
2254                         Host_Error("Corrupt clipping hull(out of range child)");
2255         }
2256 }
2257
2258 //Duplicate the drawing hull structure as a clipping hull
2259 static void Mod_Q1BSP_MakeHull0(void)
2260 {
2261         mnode_t         *in;
2262         dclipnode_t *out;
2263         int                     i;
2264         hull_t          *hull;
2265
2266         hull = &loadmodel->brushq1.hulls[0];
2267
2268         in = loadmodel->brush.data_nodes;
2269         out = (dclipnode_t *)Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_nodes * sizeof(dclipnode_t));
2270
2271         hull->clipnodes = out;
2272         hull->firstclipnode = 0;
2273         hull->lastclipnode = loadmodel->brush.num_nodes - 1;
2274         hull->planes = loadmodel->brush.data_planes;
2275
2276         for (i = 0;i < loadmodel->brush.num_nodes;i++, out++, in++)
2277         {
2278                 out->planenum = in->plane - loadmodel->brush.data_planes;
2279                 out->children[0] = in->children[0]->plane ? in->children[0] - loadmodel->brush.data_nodes : ((mleaf_t *)in->children[0])->contents;
2280                 out->children[1] = in->children[1]->plane ? in->children[1] - loadmodel->brush.data_nodes : ((mleaf_t *)in->children[1])->contents;
2281         }
2282 }
2283
2284 static void Mod_Q1BSP_LoadLeaffaces(lump_t *l)
2285 {
2286         int i, j;
2287         short *in;
2288
2289         in = (short *)(mod_base + l->fileofs);
2290         if (l->filelen % sizeof(*in))
2291                 Host_Error("Mod_Q1BSP_LoadLeaffaces: funny lump size in %s",loadmodel->name);
2292         loadmodel->brush.num_leafsurfaces = l->filelen / sizeof(*in);
2293         loadmodel->brush.data_leafsurfaces = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_leafsurfaces * sizeof(int));
2294
2295         for (i = 0;i < loadmodel->brush.num_leafsurfaces;i++)
2296         {
2297                 j = (unsigned) LittleShort(in[i]);
2298                 if (j >= loadmodel->num_surfaces)
2299                         Host_Error("Mod_Q1BSP_LoadLeaffaces: bad surface number");
2300                 loadmodel->brush.data_leafsurfaces[i] = j;
2301         }
2302 }
2303
2304 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
2305 {
2306         int             i;
2307         int             *in;
2308
2309         in = (int *)(mod_base + l->fileofs);
2310         if (l->filelen % sizeof(*in))
2311                 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
2312         loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
2313         loadmodel->brushq1.surfedges = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
2314
2315         for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
2316                 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
2317 }
2318
2319
2320 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
2321 {
2322         int                     i;
2323         mplane_t        *out;
2324         dplane_t        *in;
2325
2326         in = (dplane_t *)(mod_base + l->fileofs);
2327         if (l->filelen % sizeof(*in))
2328                 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
2329
2330         loadmodel->brush.num_planes = l->filelen / sizeof(*in);
2331         loadmodel->brush.data_planes = out = (mplane_t *)Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_planes * sizeof(*out));
2332
2333         for (i = 0;i < loadmodel->brush.num_planes;i++, in++, out++)
2334         {
2335                 out->normal[0] = LittleFloat(in->normal[0]);
2336                 out->normal[1] = LittleFloat(in->normal[1]);
2337                 out->normal[2] = LittleFloat(in->normal[2]);
2338                 out->dist = LittleFloat(in->dist);
2339
2340                 PlaneClassify(out);
2341         }
2342 }
2343
2344 static void Mod_Q1BSP_LoadMapBrushes(void)
2345 {
2346 #if 0
2347 // unfinished
2348         int submodel, numbrushes;
2349         qboolean firstbrush;
2350         char *text, *maptext;
2351         char mapfilename[MAX_QPATH];
2352         FS_StripExtension (loadmodel->name, mapfilename, sizeof (mapfilename));
2353         strlcat (mapfilename, ".map", sizeof (mapfilename));
2354         maptext = (unsigned char*) FS_LoadFile(mapfilename, tempmempool, false, NULL);
2355         if (!maptext)
2356                 return;
2357         text = maptext;
2358         if (!COM_ParseToken(&data, false))
2359                 return; // error
2360         submodel = 0;
2361         for (;;)
2362         {
2363                 if (!COM_ParseToken(&data, false))
2364                         break;
2365                 if (com_token[0] != '{')
2366                         return; // error
2367                 // entity
2368                 firstbrush = true;
2369                 numbrushes = 0;
2370                 maxbrushes = 256;
2371                 brushes = Mem_Alloc(loadmodel->mempool, maxbrushes * sizeof(mbrush_t));
2372                 for (;;)
2373                 {
2374                         if (!COM_ParseToken(&data, false))
2375                                 return; // error
2376                         if (com_token[0] == '}')
2377                                 break; // end of entity
2378                         if (com_token[0] == '{')
2379                         {
2380                                 // brush
2381                                 if (firstbrush)
2382                                 {
2383                                         if (submodel)
2384                                         {
2385                                                 if (submodel > loadmodel->brush.numsubmodels)
2386                                                 {
2387                                                         Con_Printf("Mod_Q1BSP_LoadMapBrushes: .map has more submodels than .bsp!\n");
2388                                                         model = NULL;
2389                                                 }
2390                                                 else
2391                                                         model = loadmodel->brush.submodels[submodel];
2392                                         }
2393                                         else
2394                                                 model = loadmodel;
2395                                 }
2396                                 for (;;)
2397                                 {
2398                                         if (!COM_ParseToken(&data, false))
2399                                                 return; // error
2400                                         if (com_token[0] == '}')
2401                                                 break; // end of brush
2402                                         // each brush face should be this format:
2403                                         // ( x y z ) ( x y z ) ( x y z ) texture scroll_s scroll_t rotateangle scale_s scale_t
2404                                         // FIXME: support hl .map format
2405                                         for (pointnum = 0;pointnum < 3;pointnum++)
2406                                         {
2407                                                 COM_ParseToken(&data, false);
2408                                                 for (componentnum = 0;componentnum < 3;componentnum++)
2409                                                 {
2410                                                         COM_ParseToken(&data, false);
2411                                                         point[pointnum][componentnum] = atof(com_token);
2412                                                 }
2413                                                 COM_ParseToken(&data, false);
2414                                         }
2415                                         COM_ParseToken(&data, false);
2416                                         strlcpy(facetexture, com_token, sizeof(facetexture));
2417                                         COM_ParseToken(&data, false);
2418                                         //scroll_s = atof(com_token);
2419                                         COM_ParseToken(&data, false);
2420                                         //scroll_t = atof(com_token);
2421                                         COM_ParseToken(&data, false);
2422                                         //rotate = atof(com_token);
2423                                         COM_ParseToken(&data, false);
2424                                         //scale_s = atof(com_token);
2425                                         COM_ParseToken(&data, false);
2426                                         //scale_t = atof(com_token);
2427                                         TriangleNormal(point[0], point[1], point[2], planenormal);
2428                                         VectorNormalizeDouble(planenormal);
2429                                         planedist = DotProduct(point[0], planenormal);
2430                                         //ChooseTexturePlane(planenormal, texturevector[0], texturevector[1]);
2431                                 }
2432                                 continue;
2433                         }
2434                 }
2435         }
2436 #endif
2437 }
2438
2439
2440 #define MAX_PORTALPOINTS 64
2441
2442 typedef struct portal_s
2443 {
2444         mplane_t plane;
2445         mnode_t *nodes[2];              // [0] = front side of plane
2446         struct portal_s *next[2];
2447         int numpoints;
2448         double points[3*MAX_PORTALPOINTS];
2449         struct portal_s *chain; // all portals are linked into a list
2450 }
2451 portal_t;
2452
2453 static portal_t *portalchain;
2454
2455 /*
2456 ===========
2457 AllocPortal
2458 ===========
2459 */
2460 static portal_t *AllocPortal(void)
2461 {
2462         portal_t *p;
2463         p = (portal_t *)Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2464         p->chain = portalchain;
2465         portalchain = p;
2466         return p;
2467 }
2468
2469 static void FreePortal(portal_t *p)
2470 {
2471         Mem_Free(p);
2472 }
2473
2474 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2475 {
2476         // process only nodes (leafs already had their box calculated)
2477         if (!node->plane)
2478                 return;
2479
2480         // calculate children first
2481         Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2482         Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2483
2484         // make combined bounding box from children
2485         node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2486         node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2487         node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2488         node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2489         node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2490         node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2491 }
2492
2493 static void Mod_Q1BSP_FinalizePortals(void)
2494 {
2495         int i, j, numportals, numpoints;
2496         portal_t *p, *pnext;
2497         mportal_t *portal;
2498         mvertex_t *point;
2499         mleaf_t *leaf, *endleaf;
2500
2501         // tally up portal and point counts and recalculate bounding boxes for all
2502         // leafs (because qbsp is very sloppy)
2503         leaf = loadmodel->brush.data_leafs;
2504         endleaf = leaf + loadmodel->brush.num_leafs;
2505         for (;leaf < endleaf;leaf++)
2506         {
2507                 VectorSet(leaf->mins,  2000000000,  2000000000,  2000000000);
2508                 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2509         }
2510         p = portalchain;
2511         numportals = 0;
2512         numpoints = 0;
2513         while (p)
2514         {
2515                 // note: this check must match the one below or it will usually corrupt memory
2516                 // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides
2517                 if (p->numpoints >= 3 && p->nodes[0] != p->nodes[1] && ((mleaf_t *)p->nodes[0])->clusterindex >= 0 && ((mleaf_t *)p->nodes[1])->clusterindex >= 0)
2518                 {
2519                         numportals += 2;
2520                         numpoints += p->numpoints * 2;
2521                 }
2522                 p = p->chain;
2523         }
2524         loadmodel->brush.data_portals = (mportal_t *)Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2525         loadmodel->brush.num_portals = numportals;
2526         loadmodel->brush.data_portalpoints = (mvertex_t *)((unsigned char *) loadmodel->brush.data_portals + numportals * sizeof(mportal_t));
2527         loadmodel->brush.num_portalpoints = numpoints;
2528         // clear all leaf portal chains
2529         for (i = 0;i < loadmodel->brush.num_leafs;i++)
2530                 loadmodel->brush.data_leafs[i].portals = NULL;
2531         // process all portals in the global portal chain, while freeing them
2532         portal = loadmodel->brush.data_portals;
2533         point = loadmodel->brush.data_portalpoints;
2534         p = portalchain;
2535         portalchain = NULL;
2536         while (p)
2537         {
2538                 pnext = p->chain;
2539
2540                 if (p->numpoints >= 3 && p->nodes[0] != p->nodes[1])
2541                 {
2542                         // note: this check must match the one above or it will usually corrupt memory
2543                         // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides
2544                         if (((mleaf_t *)p->nodes[0])->clusterindex >= 0 && ((mleaf_t *)p->nodes[1])->clusterindex >= 0)
2545                         {
2546                                 // first make the back to front portal(forward portal)
2547                                 portal->points = point;
2548                                 portal->numpoints = p->numpoints;
2549                                 portal->plane.dist = p->plane.dist;
2550                                 VectorCopy(p->plane.normal, portal->plane.normal);
2551                                 portal->here = (mleaf_t *)p->nodes[1];
2552                                 portal->past = (mleaf_t *)p->nodes[0];
2553                                 // copy points
2554                                 for (j = 0;j < portal->numpoints;j++)
2555                                 {
2556                                         VectorCopy(p->points + j*3, point->position);
2557                                         point++;
2558                                 }
2559                                 BoxFromPoints(portal->mins, portal->maxs, portal->numpoints, portal->points->position);
2560                                 PlaneClassify(&portal->plane);
2561
2562                                 // link into leaf's portal chain
2563                                 portal->next = portal->here->portals;
2564                                 portal->here->portals = portal;
2565
2566                                 // advance to next portal
2567                                 portal++;
2568
2569                                 // then make the front to back portal(backward portal)
2570                                 portal->points = point;
2571                                 portal->numpoints = p->numpoints;
2572                                 portal->plane.dist = -p->plane.dist;
2573                                 VectorNegate(p->plane.normal, portal->plane.normal);
2574                                 portal->here = (mleaf_t *)p->nodes[0];
2575                                 portal->past = (mleaf_t *)p->nodes[1];
2576                                 // copy points
2577                                 for (j = portal->numpoints - 1;j >= 0;j--)
2578                                 {
2579                                         VectorCopy(p->points + j*3, point->position);
2580                                         point++;
2581                                 }
2582                                 BoxFromPoints(portal->mins, portal->maxs, portal->numpoints, portal->points->position);
2583                                 PlaneClassify(&portal->plane);
2584
2585                                 // link into leaf's portal chain
2586                                 portal->next = portal->here->portals;
2587                                 portal->here->portals = portal;
2588
2589                                 // advance to next portal
2590                                 portal++;
2591                         }
2592                         // add the portal's polygon points to the leaf bounding boxes
2593                         for (i = 0;i < 2;i++)
2594                         {
2595                                 leaf = (mleaf_t *)p->nodes[i];
2596                                 for (j = 0;j < p->numpoints;j++)
2597                                 {
2598                                         if (leaf->mins[0] > p->points[j*3+0]) leaf->mins[0] = p->points[j*3+0];
2599                                         if (leaf->mins[1] > p->points[j*3+1]) leaf->mins[1] = p->points[j*3+1];
2600                                         if (leaf->mins[2] > p->points[j*3+2]) leaf->mins[2] = p->points[j*3+2];
2601                                         if (leaf->maxs[0] < p->points[j*3+0]) leaf->maxs[0] = p->points[j*3+0];
2602                                         if (leaf->maxs[1] < p->points[j*3+1]) leaf->maxs[1] = p->points[j*3+1];
2603                                         if (leaf->maxs[2] < p->points[j*3+2]) leaf->maxs[2] = p->points[j*3+2];
2604                                 }
2605                         }
2606                 }
2607                 FreePortal(p);
2608                 p = pnext;
2609         }
2610         // now recalculate the node bounding boxes from the leafs
2611         Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brush.data_nodes);
2612 }
2613
2614 /*
2615 =============
2616 AddPortalToNodes
2617 =============
2618 */
2619 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2620 {
2621         if (!front)
2622                 Host_Error("AddPortalToNodes: NULL front node");
2623         if (!back)
2624                 Host_Error("AddPortalToNodes: NULL back node");
2625         if (p->nodes[0] || p->nodes[1])
2626                 Host_Error("AddPortalToNodes: already included");
2627         // note: front == back is handled gracefully, because leaf 0 is the shared solid leaf, it can often have portals with the same leaf on both sides
2628
2629         p->nodes[0] = front;
2630         p->next[0] = (portal_t *)front->portals;
2631         front->portals = (mportal_t *)p;
2632
2633         p->nodes[1] = back;
2634         p->next[1] = (portal_t *)back->portals;
2635         back->portals = (mportal_t *)p;
2636 }
2637
2638 /*
2639 =============
2640 RemovePortalFromNode
2641 =============
2642 */
2643 static void RemovePortalFromNodes(portal_t *portal)
2644 {
2645         int i;
2646         mnode_t *node;
2647         void **portalpointer;
2648         portal_t *t;
2649         for (i = 0;i < 2;i++)
2650         {
2651                 node = portal->nodes[i];
2652
2653                 portalpointer = (void **) &node->portals;
2654                 while (1)
2655                 {
2656                         t = (portal_t *)*portalpointer;
2657                         if (!t)
2658                                 Host_Error("RemovePortalFromNodes: portal not in leaf");
2659
2660                         if (t == portal)
2661                         {
2662                                 if (portal->nodes[0] == node)
2663                                 {
2664                                         *portalpointer = portal->next[0];
2665                                         portal->nodes[0] = NULL;
2666                                 }
2667                                 else if (portal->nodes[1] == node)
2668                                 {
2669                                         *portalpointer = portal->next[1];
2670                                         portal->nodes[1] = NULL;
2671                                 }
2672                                 else
2673                                         Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2674                                 break;
2675                         }
2676
2677                         if (t->nodes[0] == node)
2678                                 portalpointer = (void **) &t->next[0];
2679                         else if (t->nodes[1] == node)
2680                                 portalpointer = (void **) &t->next[1];
2681                         else
2682                                 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2683                 }
2684         }
2685 }
2686
2687 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2688 {
2689         int i, side;
2690         mnode_t *front, *back, *other_node;
2691         mplane_t clipplane, *plane;
2692         portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2693         int numfrontpoints, numbackpoints;
2694         double frontpoints[3*MAX_PORTALPOINTS], backpoints[3*MAX_PORTALPOINTS];
2695
2696         // if a leaf, we're done
2697         if (!node->plane)
2698                 return;
2699
2700         plane = node->plane;
2701
2702         front = node->children[0];
2703         back = node->children[1];
2704         if (front == back)
2705                 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2706
2707         // create the new portal by generating a polygon for the node plane,
2708         // and clipping it by all of the other portals(which came from nodes above this one)
2709         nodeportal = AllocPortal();
2710         nodeportal->plane = *plane;
2711
2712         PolygonD_QuadForPlane(nodeportal->points, nodeportal->plane.normal[0], nodeportal->plane.normal[1], nodeportal->plane.normal[2], nodeportal->plane.dist, 1024.0*1024.0*1024.0);
2713         nodeportal->numpoints = 4;
2714         side = 0;       // shut up compiler warning
2715         for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2716         {
2717                 clipplane = portal->plane;
2718                 if (portal->nodes[0] == portal->nodes[1])
2719                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2720                 if (portal->nodes[0] == node)
2721                         side = 0;
2722                 else if (portal->nodes[1] == node)
2723                 {
2724                         clipplane.dist = -clipplane.dist;
2725                         VectorNegate(clipplane.normal, clipplane.normal);
2726                         side = 1;
2727                 }
2728                 else
2729                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2730
2731                 for (i = 0;i < nodeportal->numpoints*3;i++)
2732                         frontpoints[i] = nodeportal->points[i];
2733                 PolygonD_Divide(nodeportal->numpoints, frontpoints, clipplane.normal[0], clipplane.normal[1], clipplane.normal[2], clipplane.dist, 1.0/32.0, MAX_PORTALPOINTS, nodeportal->points, &nodeportal->numpoints, 0, NULL, NULL, NULL);
2734                 if (nodeportal->numpoints <= 0 || nodeportal->numpoints >= MAX_PORTALPOINTS)
2735                         break;
2736         }
2737
2738         if (nodeportal->numpoints < 3)
2739         {
2740                 Con_Print("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2741                 nodeportal->numpoints = 0;
2742         }
2743         else if (nodeportal->numpoints >= MAX_PORTALPOINTS)
2744         {
2745                 Con_Print("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal has too many points\n");
2746                 nodeportal->numpoints = 0;
2747         }
2748
2749         AddPortalToNodes(nodeportal, front, back);
2750
2751         // split the portals of this node along this node's plane and assign them to the children of this node
2752         // (migrating the portals downward through the tree)
2753         for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2754         {
2755                 if (portal->nodes[0] == portal->nodes[1])
2756                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2757                 if (portal->nodes[0] == node)
2758                         side = 0;
2759                 else if (portal->nodes[1] == node)
2760                         side = 1;
2761                 else
2762                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2763                 nextportal = portal->next[side];
2764                 if (!portal->numpoints)
2765                         continue;
2766
2767                 other_node = portal->nodes[!side];
2768                 RemovePortalFromNodes(portal);
2769
2770                 // cut the portal into two portals, one on each side of the node plane
2771                 PolygonD_Divide(portal->numpoints, portal->points, plane->normal[0], plane->normal[1], plane->normal[2], plane->dist, 1.0/32.0, MAX_PORTALPOINTS, frontpoints, &numfrontpoints, MAX_PORTALPOINTS, backpoints, &numbackpoints, NULL);
2772
2773                 if (!numfrontpoints)
2774                 {
2775                         if (side == 0)
2776                                 AddPortalToNodes(portal, back, other_node);
2777                         else
2778                                 AddPortalToNodes(portal, other_node, back);
2779                         continue;
2780                 }
2781                 if (!numbackpoints)
2782                 {
2783                         if (side == 0)
2784                                 AddPortalToNodes(portal, front, other_node);
2785                         else
2786                                 AddPortalToNodes(portal, other_node, front);
2787                         continue;
2788                 }
2789
2790                 // the portal is split
2791                 splitportal = AllocPortal();
2792                 temp = splitportal->chain;
2793                 *splitportal = *portal;
2794                 splitportal->chain = temp;
2795                 for (i = 0;i < numbackpoints*3;i++)
2796                         splitportal->points[i] = backpoints[i];
2797                 splitportal->numpoints = numbackpoints;
2798                 for (i = 0;i < numfrontpoints*3;i++)
2799                         portal->points[i] = frontpoints[i];
2800                 portal->numpoints = numfrontpoints;
2801
2802                 if (side == 0)
2803                 {
2804                         AddPortalToNodes(portal, front, other_node);
2805                         AddPortalToNodes(splitportal, back, other_node);
2806                 }
2807                 else
2808                 {
2809                         AddPortalToNodes(portal, other_node, front);
2810                         AddPortalToNodes(splitportal, other_node, back);
2811                 }
2812         }
2813
2814         Mod_Q1BSP_RecursiveNodePortals(front);
2815         Mod_Q1BSP_RecursiveNodePortals(back);
2816 }
2817
2818 static void Mod_Q1BSP_MakePortals(void)
2819 {
2820         portalchain = NULL;
2821         Mod_Q1BSP_RecursiveNodePortals(loadmodel->brush.data_nodes);
2822         Mod_Q1BSP_FinalizePortals();
2823 }
2824
2825 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2826 {
2827         int i, j, stylecounts[256], totalcount, remapstyles[256];
2828         msurface_t *surface;
2829         memset(stylecounts, 0, sizeof(stylecounts));
2830         for (i = 0;i < model->nummodelsurfaces;i++)
2831         {
2832                 surface = model->data_surfaces + model->firstmodelsurface + i;
2833                 for (j = 0;j < MAXLIGHTMAPS;j++)
2834                         stylecounts[surface->lightmapinfo->styles[j]]++;
2835         }
2836         totalcount = 0;
2837         model->brushq1.light_styles = 0;
2838         for (i = 0;i < 255;i++)
2839         {
2840                 if (stylecounts[i])
2841                 {
2842                         remapstyles[i] = model->brushq1.light_styles++;
2843                         totalcount += stylecounts[i] + 1;
2844                 }
2845         }
2846         if (!totalcount)
2847                 return;
2848         model->brushq1.light_style = (unsigned char *)Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(unsigned char));
2849         model->brushq1.light_stylevalue = (int *)Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2850         model->brushq1.light_styleupdatechains = (msurface_t ***)Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2851         model->brushq1.light_styleupdatechainsbuffer = (msurface_t **)Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2852         model->brushq1.light_styles = 0;
2853         for (i = 0;i < 255;i++)
2854                 if (stylecounts[i])
2855                         model->brushq1.light_style[model->brushq1.light_styles++] = i;
2856         j = 0;
2857         for (i = 0;i < model->brushq1.light_styles;i++)
2858         {
2859                 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2860                 j += stylecounts[model->brushq1.light_style[i]] + 1;
2861         }
2862         for (i = 0;i < model->nummodelsurfaces;i++)
2863         {
2864                 surface = model->data_surfaces + model->firstmodelsurface + i;
2865                 for (j = 0;j < MAXLIGHTMAPS;j++)
2866                         if (surface->lightmapinfo->styles[j] != 255)
2867                                 *model->brushq1.light_styleupdatechains[remapstyles[surface->lightmapinfo->styles[j]]]++ = surface;
2868         }
2869         j = 0;
2870         for (i = 0;i < model->brushq1.light_styles;i++)
2871         {
2872                 *model->brushq1.light_styleupdatechains[i] = NULL;
2873                 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2874                 j += stylecounts[model->brushq1.light_style[i]] + 1;
2875         }
2876 }
2877
2878 //Returns PVS data for a given point
2879 //(note: can return NULL)
2880 static unsigned char *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p)
2881 {
2882         mnode_t *node;
2883         node = model->brush.data_nodes;
2884         while (node->plane)
2885                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
2886         if (((mleaf_t *)node)->clusterindex >= 0)
2887                 return model->brush.data_pvsclusters + ((mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
2888         else
2889                 return NULL;
2890 }
2891
2892 static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, unsigned char *pvsbuffer, int pvsbytes, mnode_t *node)
2893 {
2894         while (node->plane)
2895         {
2896                 float d = PlaneDiff(org, node->plane);
2897                 if (d > radius)
2898                         node = node->children[0];
2899                 else if (d < -radius)
2900                         node = node->children[1];
2901                 else
2902                 {
2903                         // go down both sides
2904                         Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
2905                         node = node->children[1];
2906                 }
2907         }
2908         // if this leaf is in a cluster, accumulate the pvs bits
2909         if (((mleaf_t *)node)->clusterindex >= 0)
2910         {
2911                 int i;
2912                 unsigned char *pvs = model->brush.data_pvsclusters + ((mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
2913                 for (i = 0;i < pvsbytes;i++)
2914                         pvsbuffer[i] |= pvs[i];
2915         }
2916 }
2917
2918 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
2919 //of the given point.
2920 static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, unsigned char *pvsbuffer, int pvsbufferlength)
2921 {
2922         int bytes = model->brush.num_pvsclusterbytes;
2923         bytes = min(bytes, pvsbufferlength);
2924         if (r_novis.integer || !model->brush.num_pvsclusters || !Mod_Q1BSP_GetPVS(model, org))
2925         {
2926                 memset(pvsbuffer, 0xFF, bytes);
2927                 return bytes;
2928         }
2929         memset(pvsbuffer, 0, bytes);
2930         Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brush.data_nodes);
2931         return bytes;
2932 }
2933
2934 static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs)
2935 {
2936         vec3_t size;
2937         const hull_t *hull;
2938
2939         VectorSubtract(inmaxs, inmins, size);
2940         if (cmodel->brush.ismcbsp)
2941         {
2942                 if (size[0] < 3)
2943                         hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2944                 else if (size[2] < 48) // pick the nearest of 40 or 56
2945                         hull = &cmodel->brushq1.hulls[2]; // 16x16x40
2946                 else
2947                         hull = &cmodel->brushq1.hulls[1]; // 16x16x56
2948         }
2949         else if (cmodel->brush.ishlbsp)
2950         {
2951                 if (size[0] < 3)
2952                         hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2953                 else if (size[0] <= 32)
2954                 {
2955                         if (size[2] < 54) // pick the nearest of 36 or 72
2956                                 hull = &cmodel->brushq1.hulls[3]; // 32x32x36
2957                         else
2958                                 hull = &cmodel->brushq1.hulls[1]; // 32x32x72
2959                 }
2960                 else
2961                         hull = &cmodel->brushq1.hulls[2]; // 64x64x64
2962         }
2963         else
2964         {
2965                 if (size[0] < 3)
2966                         hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2967                 else if (size[0] <= 32)
2968                         hull = &cmodel->brushq1.hulls[1]; // 32x32x56
2969                 else
2970                         hull = &cmodel->brushq1.hulls[2]; // 64x64x88
2971         }
2972         VectorCopy(inmins, outmins);
2973         VectorAdd(inmins, hull->clip_size, outmaxs);
2974 }
2975
2976 void Mod_Q1BSP_Load(model_t *mod, void *buffer, void *bufferend)
2977 {
2978         int i, j, k;
2979         dheader_t *header;
2980         dmodel_t *bm;
2981         mempool_t *mainmempool;
2982         float dist, modelyawradius, modelradius, *vec;
2983         msurface_t *surface;
2984         int numshadowmeshtriangles;
2985         dheader_t _header;
2986         hullinfo_t hullinfo;
2987
2988         mod->type = mod_brushq1;
2989
2990         if (!memcmp (buffer, "MCBSPpad", 8))
2991         {
2992                 unsigned char   *index;
2993
2994                 mod->brush.ismcbsp = true;
2995                 mod->brush.ishlbsp = false;
2996
2997                 mod_base = (unsigned char*)buffer;
2998
2999                 index = mod_base;
3000                 index += 8;
3001                 i = SB_ReadInt (&index);
3002                 if (i != MCBSPVERSION)
3003                         Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i)", mod->name, i, MCBSPVERSION);
3004
3005         // read hull info
3006                 hullinfo.numhulls = LittleLong(*(int*)index); index += 4;
3007                 hullinfo.filehulls = hullinfo.numhulls;
3008                 VectorClear (hullinfo.hullsizes[0][0]);
3009                 VectorClear (hullinfo.hullsizes[0][1]);
3010                 for (i = 1; i < hullinfo.numhulls; i++)
3011                 {
3012                         hullinfo.hullsizes[i][0][0] = SB_ReadFloat (&index);
3013                         hullinfo.hullsizes[i][0][1] = SB_ReadFloat (&index);
3014                         hullinfo.hullsizes[i][0][2] = SB_ReadFloat (&index);
3015                         hullinfo.hullsizes[i][1][0] = SB_ReadFloat (&index);
3016                         hullinfo.hullsizes[i][1][1] = SB_ReadFloat (&index);
3017                         hullinfo.hullsizes[i][1][2] = SB_ReadFloat (&index);
3018                 }
3019
3020         // read lumps
3021                 _header.version = 0;
3022                 for (i = 0; i < HEADER_LUMPS; i++)
3023                 {
3024                         _header.lumps[i].fileofs = SB_ReadInt (&index);
3025                         _header.lumps[i].filelen = SB_ReadInt (&index);
3026                 }
3027
3028                 header = &_header;
3029         }
3030         else
3031         {
3032                 header = (dheader_t *)buffer;
3033
3034                 i = LittleLong(header->version);
3035                 if (i != BSPVERSION && i != 30)
3036                         Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife)", mod->name, i, BSPVERSION);
3037                 mod->brush.ishlbsp = i == 30;
3038                 mod->brush.ismcbsp = false;
3039
3040         // fill in hull info
3041                 VectorClear (hullinfo.hullsizes[0][0]);
3042                 VectorClear (hullinfo.hullsizes[0][1]);
3043                 if (mod->brush.ishlbsp)
3044                 {
3045                         hullinfo.numhulls = 4;
3046                         hullinfo.filehulls = 4;
3047                         VectorSet (hullinfo.hullsizes[1][0], -16, -16, -36);
3048                         VectorSet (hullinfo.hullsizes[1][1], 16, 16, 36);
3049                         VectorSet (hullinfo.hullsizes[2][0], -32, -32, -32);
3050                         VectorSet (hullinfo.hullsizes[2][1], 32, 32, 32);
3051                         VectorSet (hullinfo.hullsizes[3][0], -16, -16, -18);
3052                         VectorSet (hullinfo.hullsizes[3][1], 16, 16, 18);
3053                 }
3054                 else
3055                 {
3056                         hullinfo.numhulls = 3;
3057                         hullinfo.filehulls = 4;
3058                         VectorSet (hullinfo.hullsizes[1][0], -16, -16, -24);
3059                         VectorSet (hullinfo.hullsizes[1][1], 16, 16, 32);
3060                         VectorSet (hullinfo.hullsizes[2][0], -32, -32, -24);
3061                         VectorSet (hullinfo.hullsizes[2][1], 32, 32, 64);
3062                 }
3063
3064         // read lumps
3065                 mod_base = (unsigned char*)buffer;
3066                 for (i = 0; i < HEADER_LUMPS; i++)
3067                 {
3068                         header->lumps[i].fileofs = LittleLong(header->lumps[i].fileofs);
3069                         header->lumps[i].filelen = LittleLong(header->lumps[i].filelen);
3070                 }
3071         }
3072
3073         mod->soundfromcenter = true;
3074         mod->TraceBox = Mod_Q1BSP_TraceBox;
3075         mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents;
3076         mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents;
3077         mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
3078         mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
3079         mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
3080         mod->brush.BoxTouchingLeafPVS = Mod_Q1BSP_BoxTouchingLeafPVS;
3081         mod->brush.BoxTouchingVisibleLeafs = Mod_Q1BSP_BoxTouchingVisibleLeafs;
3082         mod->brush.FindBoxClusters = Mod_Q1BSP_FindBoxClusters;
3083         mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
3084         mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
3085         mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint;
3086         mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize;
3087         mod->brush.PointInLeaf = Mod_Q1BSP_PointInLeaf;
3088
3089         if (loadmodel->isworldmodel)
3090         {
3091                 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3092                 Cvar_SetValue("mcbsp", mod->brush.ismcbsp);
3093         }
3094
3095 // load into heap
3096
3097         // store which lightmap format to use
3098         mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3099
3100         mod->brush.qw_md4sum = 0;
3101         mod->brush.qw_md4sum2 = 0;
3102         for (i = 0;i < HEADER_LUMPS;i++)
3103         {
3104                 if (i == LUMP_ENTITIES)
3105                         continue;
3106                 mod->brush.qw_md4sum ^= LittleLong(Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen));
3107                 if (i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES)
3108                         continue;
3109                 mod->brush.qw_md4sum2 ^= LittleLong(Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen));
3110         }
3111
3112         Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
3113         Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
3114         Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
3115         Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
3116         Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
3117         Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
3118         Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
3119         Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
3120         Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
3121         Mod_Q1BSP_LoadLeaffaces(&header->lumps[LUMP_MARKSURFACES]);
3122         Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
3123         // load submodels before leafs because they contain the number of vis leafs
3124         Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS], &hullinfo);
3125         Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
3126         Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
3127         Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES], &hullinfo);
3128
3129         if (!mod->brushq1.lightdata)
3130                 mod->brush.LightPoint = NULL;
3131
3132         if (mod->brushq1.data_compressedpvs)
3133                 Mem_Free(mod->brushq1.data_compressedpvs);
3134         mod->brushq1.data_compressedpvs = NULL;
3135         mod->brushq1.num_compressedpvs = 0;
3136
3137         Mod_Q1BSP_MakeHull0();
3138         Mod_Q1BSP_MakePortals();
3139
3140         mod->numframes = 2;             // regular and alternate animation
3141         mod->numskins = 1;
3142
3143         mainmempool = mod->mempool;
3144
3145         // make a single combined shadow mesh to allow optimized shadow volume creation
3146         numshadowmeshtriangles = 0;
3147         for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
3148         {
3149                 surface->num_firstshadowmeshtriangle = numshadowmeshtriangles;
3150                 numshadowmeshtriangles += surface->num_triangles;
3151         }
3152         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
3153         for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
3154                 Mod_ShadowMesh_AddMesh(loadmodel->mempool, loadmodel->brush.shadowmesh, NULL, NULL, NULL, surface->groupmesh->data_vertex3f, NULL, NULL, NULL, NULL, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
3155         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true);
3156         Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
3157
3158         if (loadmodel->brush.numsubmodels)
3159                 loadmodel->brush.submodels = (model_t **)Mem_Alloc(loadmodel->mempool, loadmodel->brush.numsubmodels * sizeof(model_t *));
3160
3161         if (loadmodel->isworldmodel)
3162         {
3163                 // clear out any stale submodels or worldmodels lying around
3164                 // if we did this clear before now, an error might abort loading and
3165                 // leave things in a bad state
3166                 Mod_RemoveStaleWorldModels(loadmodel);
3167         }
3168
3169         // LordHavoc: to clear the fog around the original quake submodel code, I
3170         // will explain:
3171         // first of all, some background info on the submodels:
3172         // model 0 is the map model (the world, named maps/e1m1.bsp for example)
3173         // model 1 and higher are submodels (doors and the like, named *1, *2, etc)
3174         // now the weird for loop itself:
3175         // the loop functions in an odd way, on each iteration it sets up the
3176         // current 'mod' model (which despite the confusing code IS the model of
3177         // the number i), at the end of the loop it duplicates the model to become
3178         // the next submodel, and loops back to set up the new submodel.
3179
3180         // LordHavoc: now the explanation of my sane way (which works identically):
3181         // set up the world model, then on each submodel copy from the world model
3182         // and set up the submodel with the respective model info.
3183         for (i = 0;i < mod->brush.numsubmodels;i++)
3184         {
3185                 // LordHavoc: this code was originally at the end of this loop, but
3186                 // has been transformed to something more readable at the start here.
3187
3188                 if (i > 0)
3189                 {
3190                         char name[10];
3191                         // LordHavoc: only register submodels if it is the world
3192                         // (prevents external bsp models from replacing world submodels with
3193                         //  their own)
3194                         if (!loadmodel->isworldmodel)
3195                                 continue;
3196                         // duplicate the basic information
3197                         sprintf(name, "*%i", i);
3198                         mod = Mod_FindName(name);
3199                         // copy the base model to this one
3200                         *mod = *loadmodel;
3201                         // rename the clone back to its proper name
3202                         strcpy(mod->name, name);
3203                         // textures and memory belong to the main model
3204                         mod->texturepool = NULL;
3205                         mod->mempool = NULL;
3206                 }
3207
3208                 mod->brush.submodel = i;
3209
3210                 if (loadmodel->brush.submodels)
3211                         loadmodel->brush.submodels[i] = mod;
3212
3213                 bm = &mod->brushq1.submodels[i];
3214
3215                 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
3216                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
3217                 {
3218                         mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
3219                         mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
3220                 }
3221
3222                 mod->firstmodelsurface = bm->firstface;
3223                 mod->nummodelsurfaces = bm->numfaces;
3224
3225                 // make the model surface list (used by shadowing/lighting)
3226                 mod->surfacelist = (int *)Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->surfacelist));
3227                 for (j = 0;j < mod->nummodelsurfaces;j++)
3228                         mod->surfacelist[j] = mod->firstmodelsurface + j;
3229
3230                 // this gets altered below if sky is used
3231                 mod->DrawSky = NULL;
3232                 mod->Draw = R_Q1BSP_Draw;
3233                 mod->GetLightInfo = R_Q1BSP_GetLightInfo;
3234                 mod->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
3235                 mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
3236                 mod->DrawLight = R_Q1BSP_DrawLight;
3237                 if (i != 0)
3238                 {
3239                         mod->brush.GetPVS = NULL;
3240                         mod->brush.FatPVS = NULL;
3241                         mod->brush.BoxTouchingPVS = NULL;
3242                         mod->brush.BoxTouchingLeafPVS = NULL;
3243                         mod->brush.BoxTouchingVisibleLeafs = NULL;
3244                         mod->brush.FindBoxClusters = NULL;
3245                         mod->brush.LightPoint = NULL;
3246                         mod->brush.AmbientSoundLevelsForPoint = NULL;
3247                 }
3248                 Mod_Q1BSP_BuildLightmapUpdateChains(loadmodel->mempool, mod);
3249                 if (mod->nummodelsurfaces)
3250                 {
3251                         // LordHavoc: calculate bmodel bounding box rather than trusting what it says
3252                         mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
3253                         mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
3254                         modelyawradius = 0;
3255                         modelradius = 0;
3256                         for (j = 0, surface = &mod->data_surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surface++)
3257                         {
3258                                 // we only need to have a drawsky function if it is used(usually only on world model)
3259                                 if (surface->texture->basematerialflags & MATERIALFLAG_SKY)
3260                                         mod->DrawSky = R_Q1BSP_DrawSky;
3261                                 // calculate bounding shapes
3262                                 for (k = 0, vec = (surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex);k < surface->num_vertices;k++, vec += 3)
3263                                 {
3264                                         if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
3265                                         if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
3266                                         if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
3267                                         if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
3268                                         if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
3269                                         if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
3270                                         dist = vec[0]*vec[0]+vec[1]*vec[1];
3271                                         if (modelyawradius < dist)
3272                                                 modelyawradius = dist;
3273                                         dist += vec[2]*vec[2];
3274                                         if (modelradius < dist)
3275                                                 modelradius = dist;
3276                                 }
3277                         }
3278                         modelyawradius = sqrt(modelyawradius);
3279                         modelradius = sqrt(modelradius);
3280                         mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
3281                         mod->yawmins[2] = mod->normalmins[2];
3282                         mod->yawmaxs[2] = mod->normalmaxs[2];
3283                         mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
3284                         mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
3285                         mod->radius = modelradius;
3286                         mod->radius2 = modelradius * modelradius;
3287                 }
3288                 else
3289                 {
3290                         // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
3291                         Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadmodel->name);
3292                 }
3293                 //mod->brushq1.num_visleafs = bm->visleafs;
3294         }
3295
3296         Mod_Q1BSP_LoadMapBrushes();
3297
3298         //Mod_Q1BSP_ProcessLightList();
3299
3300         if (developer.integer)
3301                 Con_Printf("Some stats for q1bsp model \"%s\": %i faces, %i nodes, %i leafs, %i visleafs, %i visleafportals\n", loadmodel->name, loadmodel->num_surfaces, loadmodel->brush.num_nodes, loadmodel->brush.num_leafs, mod->brush.num_pvsclusters, loadmodel->brush.num_portals);
3302 }
3303
3304 static void Mod_Q2BSP_LoadEntities(lump_t *l)
3305 {
3306 }
3307
3308 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
3309 {
3310 /*
3311         d_t *in;
3312         m_t *out;
3313         int i, count;
3314
3315         in = (void *)(mod_base + l->fileofs);
3316         if (l->filelen % sizeof(*in))
3317                 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3318         count = l->filelen / sizeof(*in);
3319         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3320
3321         loadmodel-> = out;
3322         loadmodel->num = count;
3323
3324         for (i = 0;i < count;i++, in++, out++)
3325         {
3326         }
3327 */
3328 }
3329
3330 static void Mod_Q2BSP_LoadVertices(lump_t *l)
3331 {
3332 /*
3333         d_t *in;
3334         m_t *out;
3335         int i, count;
3336
3337         in = (void *)(mod_base + l->fileofs);
3338         if (l->filelen % sizeof(*in))
3339                 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3340         count = l->filelen / sizeof(*in);
3341         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3342
3343         loadmodel-> = out;
3344         loadmodel->num = count;
3345
3346         for (i = 0;i < count;i++, in++, out++)
3347         {
3348         }
3349 */
3350 }
3351
3352 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
3353 {
3354 /*
3355         d_t *in;
3356         m_t *out;
3357         int i, count;
3358
3359         in = (void *)(mod_base + l->fileofs);
3360         if (l->filelen % sizeof(*in))
3361                 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
3362         count = l->filelen / sizeof(*in);
3363         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3364
3365         loadmodel-> = out;
3366         loadmodel->num = count;
3367
3368         for (i = 0;i < count;i++, in++, out++)
3369         {
3370         }
3371 */
3372 }
3373
3374 static void Mod_Q2BSP_LoadNodes(lump_t *l)
3375 {
3376 /*
3377         d_t *in;
3378         m_t *out;
3379         int i, count;
3380
3381         in = (void *)(mod_base + l->fileofs);
3382         if (l->filelen % sizeof(*in))
3383                 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3384         count = l->filelen / sizeof(*in);
3385         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3386
3387         loadmodel-> = out;
3388         loadmodel->num = count;
3389
3390         for (i = 0;i < count;i++, in++, out++)
3391         {
3392         }
3393 */
3394 }
3395
3396 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
3397 {
3398 /*
3399         d_t *in;
3400         m_t *out;
3401         int i, count;
3402
3403         in = (void *)(mod_base + l->fileofs);
3404         if (l->filelen % sizeof(*in))
3405                 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
3406         count = l->filelen / sizeof(*in);
3407         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3408
3409         loadmodel-> = out;
3410         loadmodel->num = count;
3411
3412         for (i = 0;i < count;i++, in++, out++)
3413         {
3414         }
3415 */
3416 }
3417
3418 static void Mod_Q2BSP_LoadFaces(lump_t *l)
3419 {
3420 /*
3421         d_t *in;
3422         m_t *out;
3423         int i, count;
3424
3425         in = (void *)(mod_base + l->fileofs);
3426         if (l->filelen % sizeof(*in))
3427                 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3428         count = l->filelen / sizeof(*in);
3429         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3430
3431         loadmodel-> = out;
3432         loadmodel->num = count;
3433
3434         for (i = 0;i < count;i++, in++, out++)
3435         {
3436         }
3437 */
3438 }
3439
3440 static void Mod_Q2BSP_LoadLighting(lump_t *l)
3441 {
3442 /*
3443         d_t *in;
3444         m_t *out;
3445         int i, count;
3446
3447         in = (void *)(mod_base + l->fileofs);
3448         if (l->filelen % sizeof(*in))
3449                 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
3450         count = l->filelen / sizeof(*in);
3451         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3452
3453         loadmodel-> = out;
3454         loadmodel->num = count;
3455
3456         for (i = 0;i < count;i++, in++, out++)
3457         {
3458         }
3459 */
3460 }
3461
3462 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
3463 {
3464 /*
3465         d_t *in;
3466         m_t *out;
3467         int i, count;
3468
3469         in = (void *)(mod_base + l->fileofs);
3470         if (l->filelen % sizeof(*in))
3471                 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3472         count = l->filelen / sizeof(*in);
3473         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3474
3475         loadmodel-> = out;
3476         loadmodel->num = count;
3477
3478         for (i = 0;i < count;i++, in++, out++)
3479         {
3480         }
3481 */
3482 }
3483
3484 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3485 {
3486 /*
3487         d_t *in;
3488         m_t *out;
3489         int i, count;
3490
3491         in = (void *)(mod_base + l->fileofs);
3492         if (l->filelen % sizeof(*in))
3493                 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3494         count = l->filelen / sizeof(*in);
3495         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3496
3497         loadmodel-> = out;
3498         loadmodel->num = count;
3499
3500         for (i = 0;i < count;i++, in++, out++)
3501         {
3502         }
3503 */
3504 }
3505
3506 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3507 {
3508 /*
3509         d_t *in;
3510         m_t *out;
3511         int i, count;
3512
3513         in = (void *)(mod_base + l->fileofs);
3514         if (l->filelen % sizeof(*in))
3515                 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3516         count = l->filelen / sizeof(*in);
3517         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3518
3519         loadmodel-> = out;
3520         loadmodel->num = count;
3521
3522         for (i = 0;i < count;i++, in++, out++)
3523         {
3524         }
3525 */
3526 }
3527
3528 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3529 {
3530 /*
3531         d_t *in;
3532         m_t *out;
3533         int i, count;
3534
3535         in = (void *)(mod_base + l->fileofs);
3536         if (l->filelen % sizeof(*in))
3537                 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3538         count = l->filelen / sizeof(*in);
3539         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3540
3541         loadmodel-> = out;
3542         loadmodel->num = count;
3543
3544         for (i = 0;i < count;i++, in++, out++)
3545         {
3546         }
3547 */
3548 }
3549
3550 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3551 {
3552 /*
3553         d_t *in;
3554         m_t *out;
3555         int i, count;
3556
3557         in = (void *)(mod_base + l->fileofs);
3558         if (l->filelen % sizeof(*in))
3559                 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3560         count = l->filelen / sizeof(*in);
3561         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3562
3563         loadmodel-> = out;
3564         loadmodel->num = count;
3565
3566         for (i = 0;i < count;i++, in++, out++)
3567         {
3568         }
3569 */
3570 }
3571
3572 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3573 {
3574 /*
3575         d_t *in;
3576         m_t *out;
3577         int i, count;
3578
3579         in = (void *)(mod_base + l->fileofs);
3580         if (l->filelen % sizeof(*in))
3581                 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3582         count = l->filelen / sizeof(*in);
3583         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3584
3585         loadmodel-> = out;
3586         loadmodel->num = count;
3587
3588         for (i = 0;i < count;i++, in++, out++)
3589         {
3590         }
3591 */
3592 }
3593
3594 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3595 {
3596 /*
3597         d_t *in;
3598         m_t *out;
3599         int i, count;
3600
3601         in = (void *)(mod_base + l->fileofs);
3602         if (l->filelen % sizeof(*in))
3603                 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3604         count = l->filelen / sizeof(*in);
3605         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3606
3607         loadmodel-> = out;
3608         loadmodel->num = count;
3609
3610         for (i = 0;i < count;i++, in++, out++)
3611         {
3612         }
3613 */
3614 }
3615
3616 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3617 {
3618 /*
3619         d_t *in;
3620         m_t *out;
3621         int i, count;
3622
3623         in = (void *)(mod_base + l->fileofs);
3624         if (l->filelen % sizeof(*in))
3625                 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3626         count = l->filelen / sizeof(*in);
3627         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3628
3629         loadmodel-> = out;
3630         loadmodel->num = count;
3631
3632         for (i = 0;i < count;i++, in++, out++)
3633         {
3634         }
3635 */
3636 }
3637
3638 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3639 {
3640 /*
3641         d_t *in;
3642         m_t *out;
3643         int i, count;
3644
3645         in = (void *)(mod_base + l->fileofs);
3646         if (l->filelen % sizeof(*in))
3647                 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3648         count = l->filelen / sizeof(*in);
3649         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3650
3651         loadmodel-> = out;
3652         loadmodel->num = count;
3653
3654         for (i = 0;i < count;i++, in++, out++)
3655         {
3656         }
3657 */
3658 }
3659
3660 static void Mod_Q2BSP_LoadModels(lump_t *l)
3661 {
3662 /*
3663         d_t *in;
3664         m_t *out;
3665         int i, count;
3666
3667         in = (void *)(mod_base + l->fileofs);
3668         if (l->filelen % sizeof(*in))
3669                 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3670         count = l->filelen / sizeof(*in);
3671         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3672
3673         loadmodel-> = out;
3674         loadmodel->num = count;
3675
3676         for (i = 0;i < count;i++, in++, out++)
3677         {
3678         }
3679 */
3680 }
3681
3682 void static Mod_Q2BSP_Load(model_t *mod, void *buffer, void *bufferend)
3683 {
3684         int i;
3685         q2dheader_t *header;
3686
3687         Host_Error("Mod_Q2BSP_Load: not yet implemented");
3688
3689         mod->type = mod_brushq2;
3690
3691         header = (q2dheader_t *)buffer;
3692
3693         i = LittleLong(header->version);
3694         if (i != Q2BSPVERSION)
3695                 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3696         mod->brush.ishlbsp = false;
3697         mod->brush.ismcbsp = false;
3698         if (loadmodel->isworldmodel)
3699         {
3700                 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3701                 Cvar_SetValue("mcbsp", mod->brush.ismcbsp);
3702         }
3703
3704         mod_base = (unsigned char *)header;
3705
3706         // swap all the lumps
3707         for (i = 0;i < (int) sizeof(*header) / 4;i++)
3708                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3709
3710         // store which lightmap format to use
3711         mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3712
3713         mod->brush.qw_md4sum = 0;
3714         mod->brush.qw_md4sum2 = 0;
3715         for (i = 0;i < Q2HEADER_LUMPS;i++)
3716         {
3717                 if (i == Q2LUMP_ENTITIES)
3718                         continue;
3719                 mod->brush.qw_md4sum ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen);
3720                 if (i == Q2LUMP_VISIBILITY || i == Q2LUMP_LEAFS || i == Q2LUMP_NODES)
3721                         continue;
3722                 mod->brush.qw_md4sum2 ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen);
3723         }
3724
3725         Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3726         Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3727         Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3728         Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3729         Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3730         Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3731         Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3732         Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3733         Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3734         Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3735         Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3736         Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3737         Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3738         Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3739         Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3740         Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3741         Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3742         // LordHavoc: must go last because this makes the submodels
3743         Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3744 }
3745
3746 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents);
3747 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents);
3748
3749 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3750 {
3751         const char *data;
3752         char key[128], value[MAX_INPUTLINE];
3753         float v[3];
3754         loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3755         loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3756         loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3757         if (!l->filelen)
3758                 return;
3759         loadmodel->brush.entities = (char *)Mem_Alloc(loadmodel->mempool, l->filelen);
3760         memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3761         data = loadmodel->brush.entities;
3762         // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3763         if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3764         {
3765                 while (1)
3766                 {
3767                         if (!COM_ParseToken(&data, false))
3768                                 break; // error
3769                         if (com_token[0] == '}')
3770                                 break; // end of worldspawn
3771                         if (com_token[0] == '_')
3772                                 strcpy(key, com_token + 1);
3773                         else
3774                                 strcpy(key, com_token);
3775                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
3776                                 key[strlen(key)-1] = 0;
3777                         if (!COM_ParseToken(&data, false))
3778                                 break; // error
3779                         strcpy(value, com_token);
3780                         if (!strcmp("gridsize", key))
3781                         {
3782                                 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3783                                         VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3784                         }
3785                 }
3786         }
3787 }
3788
3789 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3790 {
3791         q3dtexture_t *in;
3792         texture_t *out;
3793         int i, count;
3794         int j, c;
3795         fssearch_t *search;
3796         char *f;
3797         const char *text;
3798         int flags, flags2, numparameters, passnumber;
3799         char shadername[Q3PATHLENGTH];
3800         char sky[Q3PATHLENGTH];
3801         char firstpasstexturename[Q3PATHLENGTH];
3802         char parameter[4][Q3PATHLENGTH];
3803
3804         in = (q3dtexture_t *)(mod_base + l->fileofs);
3805         if (l->filelen % sizeof(*in))
3806                 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3807         count = l->filelen / sizeof(*in);
3808         out = (texture_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3809
3810         loadmodel->data_textures = out;
3811         loadmodel->num_textures = count;
3812
3813         for (i = 0;i < count;i++, in++, out++)
3814         {
3815                 strlcpy (out->name, in->name, sizeof (out->name));
3816                 out->surfaceflags = LittleLong(in->surfaceflags);
3817                 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, LittleLong(in->contents));
3818                 out->surfaceparms = -1;
3819         }
3820
3821         // do a quick parse of shader files to get surfaceparms
3822         if ((search = FS_Search("scripts/*.shader", true, false)))
3823         {
3824                 for (i = 0;i < search->numfilenames;i++)
3825                 {
3826                         if ((f = (char *)FS_LoadFile(search->filenames[i], tempmempool, false, NULL)))
3827                         {
3828                                 text = f;
3829                                 while (COM_ParseToken(&text, false))
3830                                 {
3831                                         strlcpy (shadername, com_token, sizeof (shadername));
3832                                         flags = 0;
3833                                         flags2 = 0;
3834                                         sky[0] = 0;
3835                                         passnumber = 0;
3836                                         firstpasstexturename[0] = 0;
3837                                         if (COM_ParseToken(&text, false) && !strcasecmp(com_token, "{"))
3838                                         {
3839                                                 while (COM_ParseToken(&text, false))
3840                                                 {
3841                                                         if (!strcasecmp(com_token, "}"))
3842                                                                 break;
3843                                                         else if (!strcasecmp(com_token, "{"))
3844                                                         {
3845                                                                 while (COM_ParseToken(&text, false))
3846                                                                 {
3847                                                                         if (!strcasecmp(com_token, "}"))
3848                                                                                 break;
3849                                                                         if (!strcasecmp(com_token, "\n"))
3850                                                                                 continue;
3851                                                                         numparameters = 0;
3852                                                                         for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
3853                                                                         {
3854                                                                                 if (j < 4)
3855                                                                                 {
3856                                                                                         strlcpy(parameter[j], com_token, sizeof(parameter[j]));
3857                                                                                         numparameters = j + 1;
3858                                                                                 }
3859                                                                                 if (!COM_ParseToken(&text, true))
3860                                                                                         break;
3861                                                                         }
3862                                                                         if (developer.integer >= 2)
3863                                                                         {
3864                                                                                 Con_Printf("%s %i: ", shadername, passnumber);
3865                                                                                 for (j = 0;j < numparameters;j++)
3866                                                                                         Con_Printf(" %s", parameter[j]);
3867                                                                                 Con_Print("\n");
3868                                                                         }
3869                                                                         if (passnumber == 0 && numparameters >= 1)
3870                                                                         {
3871                                                                                 if (!strcasecmp(parameter[0], "blendfunc") && (flags & Q3SURFACEPARM_TRANS))
3872                                                                                 {
3873                                                                                         if (numparameters == 2 && !strcasecmp(parameter[1], "add"))
3874                                                                                                 flags2 |= Q3TEXTUREFLAG_ADDITIVE;
3875                                                                                         else if (numparameters == 3 && !strcasecmp(parameter[1], "gl_one") && !strcasecmp(parameter[2], "gl_one"))
3876                                                                                                 flags2 |= Q3TEXTUREFLAG_ADDITIVE;
3877                                                                                         else if (numparameters == 3 && !strcasecmp(parameter[1], "gl_src_alpha") && !strcasecmp(parameter[2], "gl_one"))
3878                                                                                                 flags2 |= Q3TEXTUREFLAG_ADDITIVE;
3879                                                                                 }
3880                                                                                 else if (numparameters >= 2 && (!strcasecmp(parameter[0], "map") || !strcasecmp(parameter[0], "clampmap")))
3881                                                                                         strlcpy(firstpasstexturename, parameter[1], sizeof(firstpasstexturename));
3882                                                                                 else if (numparameters >= 3 && !strcasecmp(parameter[0], "animmap"))
3883                                                                                         strlcpy(firstpasstexturename, parameter[2], sizeof(firstpasstexturename));
3884                                                                                 else if (numparameters >= 2 && !strcasecmp(parameter[0], "alphafunc"))
3885                                                                                         flags2 |= Q3TEXTUREFLAG_ALPHATEST;
3886                                                                         }
3887                                                                         // break out a level if it was }
3888                                                                         if (!strcasecmp(com_token, "}"))
3889                                                                                 break;
3890                                                                 }
3891                                                                 passnumber++;
3892                                                                 continue;
3893                                                         }
3894                                                         numparameters = 0;
3895                                                         for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
3896                                                         {
3897                                                                 if (j < 4)
3898                                                                 {
3899                                                                         strlcpy(parameter[j], com_token, sizeof(parameter[j]));
3900                                                                         numparameters = j + 1;
3901                                                                 }
3902                                                                 if (!COM_ParseToken(&text, true))
3903                                                                         break;
3904                                                         }
3905                                                         if (i == 0 && !strcasecmp(com_token, "}"))
3906                                                                 break;
3907                                                         if (developer.integer >= 2)
3908                                                         {
3909                                                                 Con_Printf("%s: ", shadername);
3910                                                                 for (j = 0;j < numparameters;j++)
3911                                                                         Con_Printf(" %s", parameter[j]);
3912                                                                 Con_Print("\n");
3913                                                         }
3914                                                         if (numparameters < 1)
3915                                                                 continue;
3916                                                         if (!strcasecmp(parameter[0], "surfaceparm") && numparameters >= 2)
3917                                                         {
3918                                                                 if (!strcasecmp(parameter[1], "alphashadow"))
3919                                                                         flags |= Q3SURFACEPARM_ALPHASHADOW;
3920                                                                 else if (!strcasecmp(parameter[1], "areaportal"))
3921                                                                         flags |= Q3SURFACEPARM_AREAPORTAL;
3922                                                                 else if (!strcasecmp(parameter[1], "clusterportal"))
3923                                                                         flags |= Q3SURFACEPARM_CLUSTERPORTAL;
3924                                                                 else if (!strcasecmp(parameter[1], "detail"))
3925                                                                         flags |= Q3SURFACEPARM_DETAIL;
3926                                                                 else if (!strcasecmp(parameter[1], "donotenter"))
3927                                                                         flags |= Q3SURFACEPARM_DONOTENTER;
3928                                                                 else if (!strcasecmp(parameter[1], "fog"))
3929                                                                         flags |= Q3SURFACEPARM_FOG;
3930                                                                 else if (!strcasecmp(parameter[1], "lava"))
3931                                                                         flags |= Q3SURFACEPARM_LAVA;
3932                                                                 else if (!strcasecmp(parameter[1], "lightfilter"))
3933                                                                         flags |= Q3SURFACEPARM_LIGHTFILTER;
3934                                                                 else if (!strcasecmp(parameter[1], "metalsteps"))
3935                                                                         flags |= Q3SURFACEPARM_METALSTEPS;
3936                                                                 else if (!strcasecmp(parameter[1], "nodamage"))
3937                                                                         flags |= Q3SURFACEPARM_NODAMAGE;
3938                                                                 else if (!strcasecmp(parameter[1], "nodlight"))
3939                                                                         flags |= Q3SURFACEPARM_NODLIGHT;
3940                                                                 else if (!strcasecmp(parameter[1], "nodraw"))
3941                                                                         flags |= Q3SURFACEPARM_NODRAW;
3942                                                                 else if (!strcasecmp(parameter[1], "nodrop"))
3943                                                                         flags |= Q3SURFACEPARM_NODROP;
3944                                                                 else if (!strcasecmp(parameter[1], "noimpact"))
3945                                                                         flags |= Q3SURFACEPARM_NOIMPACT;
3946                                                                 else if (!strcasecmp(parameter[1], "nolightmap"))
3947                                                                         flags |= Q3SURFACEPARM_NOLIGHTMAP;
3948                                                                 else if (!strcasecmp(parameter[1], "nomarks"))
3949                                                                         flags |= Q3SURFACEPARM_NOMARKS;
3950                                                                 else if (!strcasecmp(parameter[1], "nomipmaps"))
3951                                                                         flags |= Q3SURFACEPARM_NOMIPMAPS;
3952                                                                 else if (!strcasecmp(parameter[1], "nonsolid"))
3953                                                                         flags |= Q3SURFACEPARM_NONSOLID;
3954                                                                 else if (!strcasecmp(parameter[1], "origin"))
3955                                                                         flags |= Q3SURFACEPARM_ORIGIN;
3956                                                                 else if (!strcasecmp(parameter[1], "playerclip"))
3957                                                                         flags |= Q3SURFACEPARM_PLAYERCLIP;
3958                                                                 else if (!strcasecmp(parameter[1], "sky"))
3959                                                                         flags |= Q3SURFACEPARM_SKY;
3960                                                                 else if (!strcasecmp(parameter[1], "slick"))
3961                                                                         flags |= Q3SURFACEPARM_SLICK;
3962                                                                 else if (!strcasecmp(parameter[1], "slime"))
3963                                                                         flags |= Q3SURFACEPARM_SLIME;
3964                                                                 else if (!strcasecmp(parameter[1], "structural"))
3965                                                                         flags |= Q3SURFACEPARM_STRUCTURAL;
3966                                                                 else if (!strcasecmp(parameter[1], "trans"))
3967                                                                         flags |= Q3SURFACEPARM_TRANS;
3968                                                                 else if (!strcasecmp(parameter[1], "water"))
3969                                                                         flags |= Q3SURFACEPARM_WATER;
3970                                                                 else if (!strcasecmp(parameter[1], "pointlight"))
3971                                                                         flags |= Q3SURFACEPARM_POINTLIGHT;
3972                                                                 else
3973                                                                         Con_Printf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[i], parameter[1]);
3974                                                         }
3975                                                         else if (!strcasecmp(parameter[0], "sky") && numparameters >= 2)
3976                                                                 strlcpy(sky, parameter[1], sizeof(sky));
3977                                                         else if (!strcasecmp(parameter[0], "skyparms") && numparameters >= 2)
3978                                                         {
3979                                                                 if (!atoi(parameter[1]) && strcasecmp(parameter[1], "-"))
3980                                                                         strlcpy(sky, parameter[1], sizeof(sky));
3981                                                         }
3982                                                         else if (!strcasecmp(parameter[0], "cull") && numparameters >= 2)
3983                                                         {
3984                                                                 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "twosided"))
3985                                                                         flags2 |= Q3TEXTUREFLAG_TWOSIDED;
3986                                                         }
3987                                                         else if (!strcasecmp(parameter[0], "nomipmaps"))
3988                                                                 flags2 |= Q3TEXTUREFLAG_NOMIPMAPS;
3989                                                         else if (!strcasecmp(parameter[0], "nopicmip"))
3990                                                                 flags2 |= Q3TEXTUREFLAG_NOPICMIP;
3991                                                         else if (!strcasecmp(parameter[0], "deformvertexes") && numparameters >= 2)
3992                                                         {
3993                                                                 if (!strcasecmp(parameter[1], "autosprite") && numparameters == 2)
3994                                                                         flags2 |= Q3TEXTUREFLAG_AUTOSPRITE;
3995                                                                 if (!strcasecmp(parameter[1], "autosprite2") && numparameters == 2)
3996                                                                         flags2 |= Q3TEXTUREFLAG_AUTOSPRITE2;
3997                                                         }
3998                                                 }
3999                                                 // add shader to list (shadername and flags)
4000                                                 // actually here we just poke into the texture settings
4001                                                 for (j = 0, out = loadmodel->data_textures;j < loadmodel->num_textures;j++, out++)
4002                                                 {
4003                                                         if (!strcasecmp(out->name, shadername))
4004                                                         {
4005                                                                 out->surfaceparms = flags;
4006                                                                 out->textureflags = flags2;
4007                                                                 out->basematerialflags = 0;
4008                                                                 if (out->surfaceparms & Q3SURFACEPARM_NODRAW)
4009                                                                         out->basematerialflags |= MATERIALFLAG_NODRAW;
4010                                                                 else if (out->surfaceparms & Q3SURFACEPARM_SKY)
4011                                                                         out->basematerialflags |= MATERIALFLAG_SKY;
4012                                                                 else if (out->surfaceparms & Q3SURFACEPARM_LAVA)
4013                                                                         out->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_FULLBRIGHT;
4014                                                                 else if (out->surfaceparms & Q3SURFACEPARM_SLIME)
4015                                                                         out->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_WATERALPHA;
4016                                                                 else if (out->surfaceparms & Q3SURFACEPARM_WATER)
4017                                                                         out->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_WATERALPHA;
4018                                                                 else
4019                                                                         out->basematerialflags |= MATERIALFLAG_WALL;
4020                                                                 if (out->textureflags & Q3TEXTUREFLAG_ALPHATEST)
4021                                                                 {
4022                                                                         // FIXME: support alpha test?
4023                                                                         out->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_TRANSPARENT;
4024                                                                 }
4025                                                                 else if (out->surfaceparms & Q3SURFACEPARM_TRANS)
4026                                                                 {
4027                                                                         if (out->textureflags & Q3TEXTUREFLAG_ADDITIVE)
4028                                                                                 out->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_TRANSPARENT;
4029                                                                         else
4030                                                                                 out->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_TRANSPARENT;
4031                                                                 }
4032                                                                 strlcpy(out->firstpasstexturename, firstpasstexturename, sizeof(out->firstpasstexturename));
4033                                                                 if ((flags & Q3SURFACEPARM_SKY) && sky[0])
4034                                                                 {
4035                                                                         // quake3 seems to append a _ to the skybox name, so this must do so as well
4036                                                                         dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", sky);
4037                                                                 }
4038                                                         }
4039                                                 }
4040                                         }
4041                                         else
4042                                         {
4043                                                 Con_Printf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[i], com_token);
4044                                                 goto parseerror;
4045                                         }
4046                                 }
4047 parseerror:
4048                                 Mem_Free(f);
4049                         }
4050                 }
4051         }
4052
4053         c = 0;
4054         for (j = 0, out = loadmodel->data_textures;j < loadmodel->num_textures;j++, out++)
4055         {
4056                 if (out->surfaceparms == -1)
4057                 {
4058                         c++;
4059                         Con_DPrintf("%s: No shader found for texture \"%s\"\n", loadmodel->name, out->name);
4060                         out->surfaceparms = 0;
4061                         if (out->surfaceflags & Q3SURFACEFLAG_NODRAW)
4062                                 out->basematerialflags |= MATERIALFLAG_NODRAW;
4063                         else if (out->surfaceflags & Q3SURFACEFLAG_SKY)
4064                                 out->basematerialflags |= MATERIALFLAG_SKY;
4065                         else
4066                                 out->basematerialflags |= MATERIALFLAG_WALL;
4067                         // these are defaults
4068                         //if (!strncmp(out->name, "textures/skies/", 15))
4069                         //      out->surfaceparms |= Q3SURFACEPARM_SKY;
4070                         //if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk")
4071                         // || !strcmp(out->name, "nodraw") || !strcmp(out->name, "common/nodraw") || !strcmp(out->name, "textures/common/nodraw"))
4072                         //      out->surfaceparms |= Q3SURFACEPARM_NODRAW;
4073                         //if (R_TextureHasAlpha(out->skin.base))
4074                         //      out->surfaceparms |= Q3SURFACEPARM_TRANS;
4075                 }
4076                 if (cls.state != ca_dedicated)
4077                         if (!Mod_LoadSkinFrame(&out->skin, out->name, (((out->textureflags & Q3TEXTUREFLAG_NOMIPMAPS) || (out->surfaceparms & Q3SURFACEPARM_NOMIPMAPS)) ? 0 : TEXF_MIPMAP) | TEXF_ALPHA | TEXF_PRECACHE | (out->textureflags & Q3TEXTUREFLAG_NOPICMIP ? 0 : TEXF_PICMIP), false, true))
4078                                 if (!Mod_LoadSkinFrame(&out->skin, out->firstpasstexturename, (((out->textureflags & Q3TEXTUREFLAG_NOMIPMAPS) || (out->surfaceparms & Q3SURFACEPARM_NOMIPMAPS)) ? 0 : TEXF_MIPMAP) | TEXF_ALPHA | TEXF_PRECACHE | (out->textureflags & Q3TEXTUREFLAG_NOPICMIP ? 0 : TEXF_PICMIP), false, true))
4079                                         Con_Printf("%s: texture loading for shader \"%s\" failed (first layer \"%s\" not found either)\n", loadmodel->name, out->name, out->firstpasstexturename);
4080                 // no animation
4081                 out->currentframe = out;
4082         }
4083         if (c)
4084                 Con_DPrintf("%s: %i textures missing shaders\n", loadmodel->name, c);
4085 }
4086
4087 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
4088 {
4089         q3dplane_t *in;
4090         mplane_t *out;
4091         int i, count;
4092
4093         in = (q3dplane_t *)(mod_base + l->fileofs);
4094         if (l->filelen % sizeof(*in))
4095                 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
4096         count = l->filelen / sizeof(*in);
4097         out = (mplane_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4098
4099         loadmodel->brush.data_planes = out;
4100         loadmodel->brush.num_planes = count;
4101
4102         for (i = 0;i < count;i++, in++, out++)
4103         {
4104                 out->normal[0] = LittleFloat(in->normal[0]);
4105                 out->normal[1] = LittleFloat(in->normal[1]);
4106                 out->normal[2] = LittleFloat(in->normal[2]);
4107                 out->dist = LittleFloat(in->dist);
4108                 PlaneClassify(out);
4109         }
4110 }
4111
4112 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
4113 {
4114         q3dbrushside_t *in;
4115         q3mbrushside_t *out;
4116         int i, n, count;
4117
4118         in = (q3dbrushside_t *)(mod_base + l->fileofs);
4119         if (l->filelen % sizeof(*in))
4120                 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
4121         count = l->filelen / sizeof(*in);
4122         out = (q3mbrushside_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4123
4124         loadmodel->brush.data_brushsides = out;
4125         loadmodel->brush.num_brushsides = count;
4126
4127         for (i = 0;i < count;i++, in++, out++)
4128         {
4129                 n = LittleLong(in->planeindex);
4130                 if (n < 0 || n >= loadmodel->brush.num_planes)
4131                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)", n, loadmodel->brush.num_planes);
4132                 out->plane = loadmodel->brush.data_planes + n;
4133                 n = LittleLong(in->textureindex);
4134                 if (n < 0 || n >= loadmodel->num_textures)
4135                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)", n, loadmodel->num_textures);
4136                 out->texture = loadmodel->data_textures + n;
4137         }
4138 }
4139
4140 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
4141 {
4142         q3dbrush_t *in;
4143         q3mbrush_t *out;
4144         int i, j, n, c, count, maxplanes;
4145         mplane_t *planes;
4146
4147         in = (q3dbrush_t *)(mod_base + l->fileofs);
4148         if (l->filelen % sizeof(*in))
4149                 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
4150         count = l->filelen / sizeof(*in);
4151         out = (q3mbrush_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4152
4153         loadmodel->brush.data_brushes = out;
4154         loadmodel->brush.num_brushes = count;
4155
4156         maxplanes = 0;
4157         planes = NULL;
4158
4159         for (i = 0;i < count;i++, in++, out++)
4160         {
4161                 n = LittleLong(in->firstbrushside);
4162                 c = LittleLong(in->numbrushsides);
4163                 if (n < 0 || n + c > loadmodel->brush.num_brushsides)
4164                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)", n, n + c, loadmodel->brush.num_brushsides);
4165                 out->firstbrushside = loadmodel->brush.data_brushsides + n;
4166                 out->numbrushsides = c;
4167                 n = LittleLong(in->textureindex);
4168                 if (n < 0 || n >= loadmodel->num_textures)
4169                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)", n, loadmodel->num_textures);
4170                 out->texture = loadmodel->data_textures + n;
4171
4172                 // make a list of mplane_t structs to construct a colbrush from
4173                 if (maxplanes < out->numbrushsides)
4174                 {
4175                         maxplanes = out->numbrushsides;
4176                         if (planes)
4177                                 Mem_Free(planes);
4178                         planes = (mplane_t *)Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
4179                 }
4180                 for (j = 0;j < out->numbrushsides;j++)
4181                 {
4182                         VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
4183                         planes[j].dist = out->firstbrushside[j].plane->dist;
4184                 }
4185                 // make the colbrush from the planes
4186                 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents);
4187         }
4188         if (planes)
4189                 Mem_Free(planes);
4190 }
4191
4192 static void Mod_Q3BSP_LoadEffects(lump_t *l)
4193 {
4194         q3deffect_t *in;
4195         q3deffect_t *out;
4196         int i, n, count;
4197
4198         in = (q3deffect_t *)(mod_base + l->fileofs);
4199         if (l->filelen % sizeof(*in))
4200                 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
4201         count = l->filelen / sizeof(*in);
4202         out = (q3deffect_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4203
4204         loadmodel->brushq3.data_effects = out;
4205         loadmodel->brushq3.num_effects = count;
4206
4207         for (i = 0;i < count;i++, in++, out++)
4208         {
4209                 strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
4210                 n = LittleLong(in->brushindex);
4211                 if (n >= loadmodel->brush.num_brushes)
4212                 {
4213                         Con_Printf("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes), setting to -1\n", n, loadmodel->brush.num_brushes);
4214                         n = -1;
4215                 }
4216                 out->brushindex = n;
4217                 out->unknown = LittleLong(in->unknown);
4218         }
4219 }
4220
4221 static void Mod_Q3BSP_LoadVertices(lump_t *l)
4222 {
4223         q3dvertex_t *in;
4224         int i, count;
4225
4226         in = (q3dvertex_t *)(mod_base + l->fileofs);
4227         if (l->filelen % sizeof(*in))
4228                 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
4229         loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
4230         loadmodel->brushq3.data_vertex3f = (float *)Mem_Alloc(loadmodel->mempool, count * (sizeof(float) * (3 + 2 + 2 + 4)));
4231         loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + count * 3;
4232         loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + count * 2;
4233         loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_texcoordlightmap2f + count * 2;
4234
4235         for (i = 0;i < count;i++, in++)
4236         {
4237                 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
4238                 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
4239                 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
4240                 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
4241                 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
4242                 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
4243                 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
4244                 // svector/tvector are calculated later in face loading
4245                 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
4246                 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
4247                 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
4248                 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
4249         }
4250 }
4251
4252 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
4253 {
4254         int *in;
4255         int *out;
4256         int i, count;
4257
4258         in = (int *)(mod_base + l->fileofs);
4259         if (l->filelen % sizeof(int[3]))
4260                 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
4261         count = l->filelen / sizeof(*in);
4262         out = (int *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4263
4264         loadmodel->brushq3.num_triangles = count / 3;
4265         loadmodel->brushq3.data_element3i = out;
4266
4267         for (i = 0;i < count;i++, in++, out++)
4268         {
4269                 *out = LittleLong(*in);
4270                 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
4271                 {
4272                         Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
4273                         *out = 0;
4274                 }
4275         }
4276 }
4277
4278 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
4279 {
4280         q3dlightmap_t *in;
4281         rtexture_t **out;
4282         int i, count;
4283
4284         if (!l->filelen)
4285                 return;
4286         in = (q3dlightmap_t *)(mod_base + l->fileofs);
4287         if (l->filelen % sizeof(*in))
4288                 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
4289         count = l->filelen / sizeof(*in);
4290         out = (rtexture_t **)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4291
4292         loadmodel->brushq3.data_lightmaps = out;
4293         loadmodel->brushq3.num_lightmaps = count;
4294
4295         loadmodel->brushq3.deluxemapping_modelspace = false;
4296         for (i = 0;i < count;i++, in++, out++)
4297         {
4298                 // if this may be a deluxemap, check if it's in modelspace or not
4299                 if ((i & 1) && !loadmodel->brushq3.deluxemapping_modelspace)
4300                 {
4301                         int j;
4302                         unsigned char *b = in->rgb;
4303                         for (j = 2;j < 128*128*3;j += 3)
4304                         {
4305                                 // if this is definitely negative Z, it is not facing outward,
4306                                 // and thus must be in modelspace, as negative Z would never
4307                                 // occur in tangentspace
4308                                 if (b[j] < 120)
4309                                 {
4310                                         loadmodel->brushq3.deluxemapping_modelspace = true;
4311                                         break;
4312                                 }
4313                         }
4314                 }
4315                 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
4316         }
4317 }
4318
4319 static void Mod_Q3BSP_LoadFaces(lump_t *l)
4320 {
4321         q3dface_t *in, *oldin;
4322         msurface_t *out, *oldout;
4323         int i, oldi, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xtess, ytess, finalvertices, finaltriangles, firstvertex, firstelement, type, oldnumtriangles, oldnumtriangles2, meshnum, meshvertices, meshtriangles, numvertices, numtriangles;
4324         //int *originalelement3i;
4325         //int *originalneighbor3i;
4326         float *originalvertex3f;
4327         //float *originalsvector3f;
4328         //float *originaltvector3f;
4329         //float *originalnormal3f;
4330         float *originalcolor4f;
4331         float *originaltexcoordtexture2f;
4332         float *originaltexcoordlightmap2f;
4333         float *v;
4334         surfmesh_t *mesh, *tempmeshlist[1024];
4335
4336         in = (q3dface_t *)(mod_base + l->fileofs);
4337         if (l->filelen % sizeof(*in))
4338                 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
4339         count = l->filelen / sizeof(*in);
4340         out = (msurface_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4341
4342         loadmodel->data_surfaces = out;
4343         loadmodel->num_surfaces = count;
4344
4345         // deluxemapped q3bsp files have an even number of lightmaps, and surfaces
4346         // always index even numbered ones (0, 2, 4, ...), the odd numbered
4347         // lightmaps are the deluxemaps (light direction textures), so if we
4348         // encounter any odd numbered lightmaps it is not a deluxemapped bsp, it
4349         // is also not a deluxemapped bsp if it has an odd number of lightmaps or
4350         // less than 2
4351         loadmodel->brushq3.deluxemapping = true;
4352         if (count >= 2 && !(count & 1))
4353         {
4354                 for (i = 0;i < count;i++)
4355                 {
4356                         n = LittleLong(in[i].lightmapindex);
4357                         if (n >= 0 && ((n & 1) || n + 1 >= loadmodel->brushq3.num_lightmaps))
4358                         {
4359                                 loadmodel->brushq3.deluxemapping = false;
4360                                 break;
4361                         }
4362                 }
4363         }
4364         else
4365                 loadmodel->brushq3.deluxemapping = false;
4366         Con_DPrintf("%s is %sdeluxemapped\n", loadmodel->name, loadmodel->brushq3.deluxemapping ? "" : "not ");
4367
4368         i = 0;
4369         for (meshnum = 0;i < count;meshnum++)
4370         {
4371                 oldi = i;
4372                 oldin = in;
4373                 oldout = out;
4374                 meshvertices = 0;
4375                 meshtriangles = 0;
4376                 for (;i < count;i++, in++, out++)
4377                 {
4378                         // check face type first
4379                         type = LittleLong(in->type);
4380                         if (type != Q3FACETYPE_POLYGON
4381                          && type != Q3FACETYPE_PATCH
4382                          && type != Q3FACETYPE_MESH
4383                          && type != Q3FACETYPE_FLARE)
4384                         {
4385                                 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, type);
4386                                 continue;
4387                         }
4388
4389                         n = LittleLong(in->textureindex);
4390                         if (n < 0 || n >= loadmodel->num_textures)
4391                         {
4392                                 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->num_textures);
4393                                 continue;
4394                         }
4395                         out->texture = loadmodel->data_textures + n;
4396                         n = LittleLong(in->effectindex);
4397                         if (n < -1 || n >= loadmodel->brushq3.num_effects)
4398                         {
4399                                 if (developer.integer >= 2)
4400                                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
4401                                 n = -1;
4402                         }
4403                         if (n == -1)
4404                                 out->effect = NULL;
4405                         else
4406                                 out->effect = loadmodel->brushq3.data_effects + n;
4407                         n = LittleLong(in->lightmapindex);
4408                         if (n >= loadmodel->brushq3.num_lightmaps)
4409                         {
4410                                 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
4411                                 n = -1;
4412                         }
4413                         else if (n < 0)
4414                                 n = -1;
4415                         if (n == -1)
4416                         {
4417                                 out->lightmaptexture = NULL;
4418                                 out->deluxemaptexture = r_texture_blanknormalmap;
4419                         }
4420                         else
4421                         {
4422                                 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
4423                                 if (loadmodel->brushq3.deluxemapping)
4424                                         out->deluxemaptexture = loadmodel->brushq3.data_lightmaps[n+1];
4425                                 else
4426                                         out->deluxemaptexture = r_texture_blanknormalmap;
4427                         }
4428
4429                         firstvertex = LittleLong(in->firstvertex);
4430                         numvertices = LittleLong(in->numvertices);
4431                         firstelement = LittleLong(in->firstelement);
4432                         numtriangles = LittleLong(in->numelements) / 3;
4433                         if (numtriangles * 3 != LittleLong(in->numelements))
4434                         {
4435                                 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, LittleLong(in->numelements));
4436                                 continue;
4437                         }
4438                         if (firstvertex < 0 || firstvertex + numvertices > loadmodel->brushq3.num_vertices)
4439                         {
4440                                 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid vertex range %i : %i (%i vertices)\n", i, out->texture->name, firstvertex, firstvertex + numvertices, loadmodel->brushq3.num_vertices);
4441                                 continue;
4442                         }
4443                         if (firstelement < 0 || firstelement + numtriangles * 3 > loadmodel->brushq3.num_triangles * 3)
4444                         {
4445                                 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid element range %i : %i (%i elements)\n", i, out->texture->name, firstelement, firstelement + numtriangles * 3, loadmodel->brushq3.num_triangles * 3);
4446                                 continue;
4447                         }
4448                         switch(type)
4449                         {
4450                         case Q3FACETYPE_POLYGON:
4451                         case Q3FACETYPE_MESH:
4452                                 // no processing necessary
4453                                 break;
4454                         case Q3FACETYPE_PATCH:
4455                                 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
4456                                 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
4457                                 if (numvertices != (patchsize[0] * patchsize[1]) || patchsize[0] < 3 || patchsize[1] < 3 || !(patchsize[0] & 1) || !(patchsize[1] & 1) || patchsize[0] * patchsize[1] >= min(r_subdivisions_maxvertices.integer, r_subdivisions_collision_maxvertices.integer))
4458                                 {
4459                                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
4460                                         continue;
4461                                 }
4462                                 originalvertex3f = loadmodel->brushq3.data_vertex3f + firstvertex * 3;
4463                                 // convert patch to Q3FACETYPE_MESH
4464                                 xtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
4465                                 ytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
4466                                 // bound to user settings
4467                                 xtess = bound(r_subdivisions_mintess.integer, xtess, r_subdivisions_maxtess.integer);
4468                                 ytess = bound(r_subdivisions_mintess.integer, ytess, r_subdivisions_maxtess.integer);
4469                                 // bound to sanity settings
4470                                 xtess = bound(1, xtess, 1024);
4471                                 ytess = bound(1, ytess, 1024);
4472                                 // bound to user limit on vertices
4473                                 while ((xtess > 1 || ytess > 1) && (((patchsize[0] - 1) * xtess) + 1) * (((patchsize[1] - 1) * ytess) + 1) > min(r_subdivisions_maxvertices.integer, 262144))
4474                                 {
4475                                         if (xtess > ytess)
4476                                                 xtess--;
4477                                         else
4478                                                 ytess--;
4479                                 }
4480                                 finalwidth = ((patchsize[0] - 1) * xtess) + 1;
4481                                 finalheight = ((patchsize[1] - 1) * ytess) + 1;
4482                                 numvertices = finalwidth * finalheight;
4483                                 numtriangles = (finalwidth - 1) * (finalheight - 1) * 2;
4484                                 break;
4485                         case Q3FACETYPE_FLARE:
4486                                 if (developer.integer >= 2)
4487                                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
4488                                 // don't render it
4489                                 continue;
4490                         }
4491                         out->num_vertices = numvertices;
4492                         out->num_triangles = numtriangles;
4493                         if (meshvertices + out->num_vertices > 65536)
4494                                 break;
4495                         meshvertices += out->num_vertices;
4496                         meshtriangles += out->num_triangles;
4497                 }
4498
4499                 i = oldi;
4500                 in = oldin;
4501                 out = oldout;
4502                 mesh = tempmeshlist[meshnum] = Mod_AllocSurfMesh(loadmodel->mempool, meshvertices, meshtriangles, false, true, false);
4503                 meshvertices = 0;
4504                 meshtriangles = 0;
4505                 for (;i < count && meshvertices + out->num_vertices <= mesh->num_vertices;i++, in++, out++)
4506                 {
4507                         if (out->num_vertices < 3 || out->num_triangles < 1)
4508                                 continue;
4509
4510                         type = LittleLong(in->type);
4511                         firstvertex = LittleLong(in->firstvertex);
4512                         firstelement = LittleLong(in->firstelement);
4513                         out->groupmesh = mesh;
4514                         out->num_firstvertex = meshvertices;
4515                         out->num_firsttriangle = meshtriangles;
4516                         switch(type)
4517                         {
4518                         case Q3FACETYPE_POLYGON:
4519                         case Q3FACETYPE_MESH:
4520                                 // no processing necessary
4521                                 for (j = 0;j < out->num_vertices;j++)
4522                                 {
4523                                         (out->groupmesh->data_vertex3f + 3 * out->num_firstvertex)[j * 3 + 0] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 0];
4524                                         (out->groupmesh->data_vertex3f + 3 * out->num_firstvertex)[j * 3 + 1] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 1];
4525                                         (out->groupmesh->data_vertex3f + 3 * out->num_firstvertex)[j * 3 + 2] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 2];
4526                                         (out->groupmesh->data_texcoordtexture2f + 2 * out->num_firstvertex)[j * 2 + 0] = loadmodel->brushq3.data_texcoordtexture2f[(firstvertex + j) * 2 + 0];
4527                                         (out->groupmesh->data_texcoordtexture2f + 2 * out->num_firstvertex)[j * 2 + 1] = loadmodel->brushq3.data_texcoordtexture2f[(firstvertex + j) * 2 + 1];
4528                                         (out->groupmesh->data_texcoordlightmap2f + 2 * out->num_firstvertex)[j * 2 + 0] = loadmodel->brushq3.data_texcoordlightmap2f[(firstvertex + j) * 2 + 0];
4529                                         (out->groupmesh->data_texcoordlightmap2f + 2 * out->num_firstvertex)[j * 2 + 1] = loadmodel->brushq3.data_texcoordlightmap2f[(firstvertex + j) * 2 + 1];
4530                                         (out->groupmesh->data_lightmapcolor4f + 4 * out->num_firstvertex)[j * 4 + 0] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 0];
4531                                         (out->groupmesh->data_lightmapcolor4f + 4 * out->num_firstvertex)[j * 4 + 1] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 1];
4532                                         (out->groupmesh->data_lightmapcolor4f + 4 * out->num_firstvertex)[j * 4 + 2] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 2];
4533                                         (out->groupmesh->data_lightmapcolor4f + 4 * out->num_firstvertex)[j * 4 + 3] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 3];
4534                                 }
4535                                 for (j = 0;j < out->num_triangles*3;j++)
4536                                         (out->groupmesh->data_element3i + 3 * out->num_firsttriangle)[j] = loadmodel->brushq3.data_element3i[firstelement + j] + out->num_firstvertex;
4537                                 break;
4538                         case Q3FACETYPE_PATCH:
4539                                 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
4540                                 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
4541                                 originalvertex3f = loadmodel->brushq3.data_vertex3f + firstvertex * 3;
4542                                 originaltexcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + firstvertex * 2;
4543                                 originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + firstvertex * 2;
4544                                 originalcolor4f = loadmodel->brushq3.data_color4f + firstvertex * 4;
4545                                 // convert patch to Q3FACETYPE_MESH
4546                                 xtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
4547                                 ytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
4548                                 // bound to user settings
4549                                 xtess = bound(r_subdivisions_mintess.integer, xtess, r_subdivisions_maxtess.integer);
4550                                 ytess = bound(r_subdivisions_mintess.integer, ytess, r_subdivisions_maxtess.integer);
4551                                 // bound to sanity settings
4552                                 xtess = bound(1, xtess, 1024);
4553                                 ytess = bound(1, ytess, 1024);
4554                                 // bound to user limit on vertices
4555                                 while ((xtess > 1 || ytess > 1) && (((patchsize[0] - 1) * xtess) + 1) * (((patchsize[1] - 1) * ytess) + 1) > min(r_subdivisions_maxvertices.integer, 262144))
4556                                 {
4557                                         if (xtess > ytess)
4558                                                 xtess--;
4559                                         else
4560                                                 ytess--;
4561                                 }
4562                                 finalwidth = ((patchsize[0] - 1) * xtess) + 1;
4563                                 finalheight = ((patchsize[1] - 1) * ytess) + 1;
4564                                 finalvertices = finalwidth * finalheight;
4565                                 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
4566                                 type = Q3FACETYPE_MESH;
4567                                 // generate geometry
4568                                 // (note: normals are skipped because they get recalculated)
4569                                 Q3PatchTesselateFloat(3, sizeof(float[3]), (out->groupmesh->data_vertex3f + 3 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[3]), originalvertex3f, xtess, ytess);
4570                                 Q3PatchTesselateFloat(2, sizeof(float[2]), (out->groupmesh->data_texcoordtexture2f + 2 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[2]), originaltexcoordtexture2f, xtess, ytess);
4571                                 Q3PatchTesselateFloat(2, sizeof(float[2]), (out->groupmesh->data_texcoordlightmap2f + 2 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[2]), originaltexcoordlightmap2f, xtess, ytess);
4572                                 Q3PatchTesselateFloat(4, sizeof(float[4]), (out->groupmesh->data_lightmapcolor4f + 4 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[4]), originalcolor4f, xtess, ytess);
4573                                 Q3PatchTriangleElements((out->groupmesh->data_element3i + 3 * out->num_firsttriangle), finalwidth, finalheight, out->num_firstvertex);
4574                                 out->num_triangles = Mod_RemoveDegenerateTriangles(out->num_triangles, (out->groupmesh->data_element3i + 3 * out->num_firsttriangle), (out->groupmesh->data_element3i + 3 * out->num_firsttriangle), out->groupmesh->data_vertex3f);
4575                                 if (developer.integer >= 2)
4576                                 {
4577                                         if (out->num_triangles < finaltriangles)
4578                                                 Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles, %i degenerate triangles removed (leaving %i)\n", patchsize[0], patchsize[1], out->num_vertices, finaltriangles, finaltriangles - out->num_triangles, out->num_triangles);
4579                                         else
4580                                                 Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles\n", patchsize[0], patchsize[1], out->num_vertices, out->num_triangles);
4581                                 }
4582                                 // q3map does not put in collision brushes for curves... ugh
4583                                 // build the lower quality collision geometry
4584                                 xtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value);
4585                                 ytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value);
4586                                 // bound to user settings
4587                                 xtess = bound(r_subdivisions_collision_mintess.integer, xtess, r_subdivisions_collision_maxtess.integer);
4588                                 ytess = bound(r_subdivisions_collision_mintess.integer, ytess, r_subdivisions_collision_maxtess.integer);
4589                                 // bound to sanity settings
4590                                 xtess = bound(1, xtess, 1024);
4591                                 ytess = bound(1, ytess, 1024);
4592                                 // bound to user limit on vertices
4593                                 while ((xtess > 1 || ytess > 1) && (((patchsize[0] - 1) * xtess) + 1) * (((patchsize[1] - 1) * ytess) + 1) > min(r_subdivisions_collision_maxvertices.integer, 262144))
4594                                 {
4595                                         if (xtess > ytess)
4596                                                 xtess--;
4597                                         else
4598                                                 ytess--;
4599                                 }
4600                                 finalwidth = ((patchsize[0] - 1) * xtess) + 1;
4601                                 finalheight = ((patchsize[1] - 1) * ytess) + 1;
4602                                 finalvertices = finalwidth * finalheight;
4603                                 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
4604
4605                                 out->data_collisionvertex3f = (float *)Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * finalvertices);
4606                                 out->data_collisionelement3i = (int *)Mem_Alloc(loadmodel->mempool, sizeof(int[3]) * finaltriangles);
4607                                 out->num_collisionvertices = finalvertices;
4608                                 out->num_collisiontriangles = finaltriangles;
4609                                 Q3PatchTesselateFloat(3, sizeof(float[3]), out->data_collisionvertex3f, patchsize[0], patchsize[1], sizeof(float[3]), originalvertex3f, xtess, ytess);
4610                                 Q3PatchTriangleElements(out->data_collisionelement3i, finalwidth, finalheight, 0);
4611
4612                                 //Mod_SnapVertices(3, out->num_vertices, (out->groupmesh->data_vertex3f + 3 * out->num_firstvertex), 0.25);
4613                                 Mod_SnapVertices(3, out->num_collisionvertices, out->data_collisionvertex3f, 1);
4614
4615                                 oldnumtriangles = out->num_triangles;
4616                                 oldnumtriangles2 = out->num_collisiontriangles;
4617                                 out->num_collisiontriangles = Mod_RemoveDegenerateTriangles(out->num_collisiontriangles, out->data_collisionelement3i, out->data_collisionelement3i, out->data_collisionvertex3f);
4618                                 if (developer.integer)
4619                                         Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve became %i:%i vertices / %i:%i triangles (%i:%i degenerate)\n", patchsize[0], patchsize[1], out->num_vertices, out->num_collisionvertices, oldnumtriangles, oldnumtriangles2, oldnumtriangles - out->num_triangles, oldnumtriangles2 - out->num_collisiontriangles);
4620                                 break;
4621                         default:
4622                                 break;
4623                         }
4624                         meshvertices += out->num_vertices;
4625                         meshtriangles += out->num_triangles;
4626                         for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++)
4627                                 if ((out->groupmesh->data_element3i + 3 * out->num_firsttriangle)[j] < out->num_firstvertex || (out->groupmesh->data_element3i + 3 * out->num_firsttriangle)[j] >= out->num_firstvertex + out->num_vertices)
4628                                         invalidelements++;
4629                         if (invalidelements)
4630                         {
4631                                 Con_Printf("Mod_Q3BSP_LoadFaces: Warning: face #%i has %i invalid elements, type = %i, texture->name = \"%s\", texture->surfaceflags = %i, firstvertex = %i, numvertices = %i, firstelement = %i, numelements = %i, elements list:\n", i, invalidelements, type, out->texture->name, out->texture->surfaceflags, firstvertex, out->num_vertices, firstelement, out->num_triangles * 3);
4632                                 for (j = 0;j < out->num_triangles * 3;j++)
4633                                 {
4634                                         Con_Printf(" %i", (out->groupmesh->data_element3i + 3 * out->num_firsttriangle)[j] - out->num_firstvertex);
4635                                         if ((out->groupmesh->data_element3i + 3 * out->num_firsttriangle)[j] < out->num_firstvertex || (out->groupmesh->data_element3i + 3 * out->num_firsttriangle)[j] >= out->num_firstvertex + out->num_vertices)
4636                                                 (out->groupmesh->data_element3i + 3 * out->num_firsttriangle)[j] = out->num_firstvertex;
4637                                 }
4638                                 Con_Print("\n");
4639                         }
4640                         // for per pixel lighting
4641                         Mod_BuildTextureVectorsAndNormals(out->num_firstvertex, out->num_vertices, out->num_triangles, out->groupmesh->data_vertex3f, out->groupmesh->data_texcoordtexture2f, (out->groupmesh->data_element3i + 3 * out->num_firsttriangle), out->groupmesh->data_svector3f, out->groupmesh->data_tvector3f, out->groupmesh->data_normal3f, true);
4642                         // calculate a bounding box
4643                         VectorClear(out->mins);
4644                         VectorClear(out->maxs);
4645                         if (out->num_vertices)
4646                         {
4647                                 VectorCopy((out->groupmesh->data_vertex3f + 3 * out->num_firstvertex), out->mins);
4648                                 VectorCopy((out->groupmesh->data_vertex3f + 3 * out->num_firstvertex), out->maxs);
4649                                 for (j = 1, v = (out->groupmesh->data_vertex3f + 3 * out->num_firstvertex) + 3;j < out->num_vertices;j++, v += 3)
4650                                 {
4651                                         out->mins[0] = min(out->mins[0], v[0]);
4652                                         out->maxs[0] = max(out->maxs[0], v[0]);
4653                                         out->mins[1] = min(out->mins[1], v[1]);
4654                                         out->maxs[1] = max(out->maxs[1], v[1]);
4655                                         out->mins[2] = min(out->mins[2], v[2]);
4656                                         out->maxs[2] = max(out->maxs[2], v[2]);
4657                                 }
4658                                 out->mins[0] -= 1.0f;
4659                                 out->mins[1] -= 1.0f;
4660                                 out->mins[2] -= 1.0f;
4661                                 out->maxs[0] += 1.0f;
4662                                 out->maxs[1] += 1.0f;
4663                                 out->maxs[2] += 1.0f;
4664                         }
4665                         // set lightmap styles for consistency with q1bsp
4666                         //out->lightmapinfo->styles[0] = 0;
4667                         //out->lightmapinfo->styles[1] = 255;
4668                         //out->lightmapinfo->styles[2] = 255;
4669                         //out->lightmapinfo->styles[3] = 255;
4670                 }
4671         }
4672
4673         // now store the completed list of meshes
4674         loadmodel->nummeshes = meshnum;
4675         if (loadmodel->nummeshes)
4676         {
4677                 loadmodel->meshlist = (surfmesh_t **)Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t *) * loadmodel->nummeshes);
4678                 memcpy(loadmodel->meshlist, tempmeshlist, sizeof(surfmesh_t *) * loadmodel->nummeshes);
4679         }
4680
4681         // free the no longer needed vertex data
4682         loadmodel->brushq3.num_vertices = 0;
4683         Mem_Free(loadmodel->brushq3.data_vertex3f);
4684         loadmodel->brushq3.data_vertex3f = NULL;
4685         loadmodel->brushq3.data_texcoordtexture2f = NULL;
4686         loadmodel->brushq3.data_texcoordlightmap2f = NULL;
4687         loadmodel->brushq3.data_color4f = NULL;
4688         // free the no longer needed triangle data
4689         loadmodel->brushq3.num_triangles = 0;
4690         Mem_Free(loadmodel->brushq3.data_element3i);
4691         loadmodel->brushq3.data_element3i = NULL;
4692 }
4693
4694 static void Mod_Q3BSP_LoadModels(lump_t *l)
4695 {
4696         q3dmodel_t *in;
4697         q3dmodel_t *out;
4698         int i, j, n, c, count;
4699
4700         in = (q3dmodel_t *)(mod_base + l->fileofs);
4701         if (l->filelen % sizeof(*in))
4702                 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
4703         count = l->filelen / sizeof(*in);
4704         out = (q3dmodel_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4705
4706         loadmodel->brushq3.data_models = out;
4707         loadmodel->brushq3.num_models = count;
4708
4709         for (i = 0;i < count;i++, in++, out++)
4710         {
4711                 for (j = 0;j < 3;j++)
4712                 {
4713                         out->mins[j] = LittleFloat(in->mins[j]);
4714                         out->maxs[j] = LittleFloat(in->maxs[j]);
4715                 }
4716                 n = LittleLong(in->firstface);
4717                 c = LittleLong(in->numfaces);
4718                 if (n < 0 || n + c > loadmodel->num_surfaces)
4719                         Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)", n, n + c, loadmodel->num_surfaces);
4720                 out->firstface = n;
4721                 out->numfaces = c;
4722                 n = LittleLong(in->firstbrush);
4723                 c = LittleLong(in->numbrushes);
4724                 if (n < 0 || n + c > loadmodel->brush.num_brushes)
4725                         Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)", n, n + c, loadmodel->brush.num_brushes);
4726                 out->firstbrush = n;
4727                 out->numbrushes = c;
4728         }
4729 }
4730
4731 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
4732 {
4733         int *in;
4734         int *out;
4735         int i, n, count;
4736
4737         in = (int *)(mod_base + l->fileofs);
4738         if (l->filelen % sizeof(*in))
4739                 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
4740         count = l->filelen / sizeof(*in);
4741         out = (int *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4742
4743         loadmodel->brush.data_leafbrushes = out;
4744         loadmodel->brush.num_leafbrushes = count;
4745
4746         for (i = 0;i < count;i++, in++, out++)
4747         {
4748                 n = LittleLong(*in);
4749                 if (n < 0 || n >= loadmodel->brush.num_brushes)
4750                         Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)", n, loadmodel->brush.num_brushes);
4751                 *out = n;
4752         }
4753 }
4754
4755 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
4756 {
4757         int *in;
4758         int *out;
4759         int i, n, count;
4760
4761         in = (int *)(mod_base + l->fileofs);
4762         if (l->filelen % sizeof(*in))
4763                 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
4764         count = l->filelen / sizeof(*in);
4765         out = (int *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4766
4767         loadmodel->brush.data_leafsurfaces = out;
4768         loadmodel->brush.num_leafsurfaces = count;
4769
4770         for (i = 0;i < count;i++, in++, out++)
4771         {
4772                 n = LittleLong(*in);
4773                 if (n < 0 || n >= loadmodel->num_surfaces)
4774                         Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)", n, loadmodel->num_surfaces);
4775                 *out = n;
4776         }
4777 }
4778
4779 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
4780 {
4781         q3dleaf_t *in;
4782         mleaf_t *out;
4783         int i, j, n, c, count;
4784
4785         in = (q3dleaf_t *)(mod_base + l->fileofs);
4786         if (l->filelen % sizeof(*in))
4787                 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
4788         count = l->filelen / sizeof(*in);
4789         out = (mleaf_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4790
4791         loadmodel->brush.data_leafs = out;
4792         loadmodel->brush.num_leafs = count;
4793
4794         for (i = 0;i < count;i++, in++, out++)
4795         {
4796                 out->parent = NULL;
4797                 out->plane = NULL;
4798                 out->clusterindex = LittleLong(in->clusterindex);
4799                 out->areaindex = LittleLong(in->areaindex);
4800                 for (j = 0;j < 3;j++)
4801                 {
4802                         // yes the mins/maxs are ints
4803                         out->mins[j] = LittleLong(in->mins[j]) - 1;
4804                         out->maxs[j] = LittleLong(in->maxs[j]) + 1;
4805                 }
4806                 n = LittleLong(in->firstleafface);
4807                 c = LittleLong(in->numleaffaces);
4808                 if (n < 0 || n + c > loadmodel->brush.num_leafsurfaces)
4809                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafsurface range %i : %i (%i leafsurfaces)", n, n + c, loadmodel->brush.num_leafsurfaces);
4810                 out->firstleafsurface = loadmodel->brush.data_leafsurfaces + n;
4811                 out->numleafsurfaces = c;
4812                 n = LittleLong(in->firstleafbrush);
4813                 c = LittleLong(in->numleafbrushes);
4814                 if (n < 0 || n + c > loadmodel->brush.num_leafbrushes)
4815                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)", n, n + c, loadmodel->brush.num_leafbrushes);
4816                 out->firstleafbrush = loadmodel->brush.data_leafbrushes + n;
4817                 out->numleafbrushes = c;
4818         }
4819 }
4820
4821 static void Mod_Q3BSP_LoadNodes(lump_t *l)
4822 {
4823         q3dnode_t *in;
4824         mnode_t *out;
4825         int i, j, n, count;
4826
4827         in = (q3dnode_t *)(mod_base + l->fileofs);
4828         if (l->filelen % sizeof(*in))
4829                 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
4830         count = l->filelen / sizeof(*in);
4831         out = (mnode_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4832
4833         loadmodel->brush.data_nodes = out;
4834         loadmodel->brush.num_nodes = count;
4835
4836         for (i = 0;i < count;i++, in++, out++)
4837         {
4838                 out->parent = NULL;
4839                 n = LittleLong(in->planeindex);
4840                 if (n < 0 || n >= loadmodel->brush.num_planes)
4841                         Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)", n, loadmodel->brush.num_planes);
4842                 out->plane = loadmodel->brush.data_planes + n;
4843                 for (j = 0;j < 2;j++)
4844                 {
4845                         n = LittleLong(in->childrenindex[j]);
4846                         if (n >= 0)
4847                         {
4848                                 if (n >= loadmodel->brush.num_nodes)
4849                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)", n, loadmodel->brush.num_nodes);
4850                                 out->children[j] = loadmodel->brush.data_nodes + n;
4851                         }
4852                         else
4853                         {
4854                                 n = -1 - n;
4855                                 if (n >= loadmodel->brush.num_leafs)
4856                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)", n, loadmodel->brush.num_leafs);
4857                                 out->children[j] = (mnode_t *)(loadmodel->brush.data_leafs + n);
4858                         }
4859                 }
4860                 for (j = 0;j < 3;j++)
4861                 {
4862                         // yes the mins/maxs are ints
4863                         out->mins[j] = LittleLong(in->mins[j]) - 1;
4864                         out->maxs[j] = LittleLong(in->maxs[j]) + 1;
4865                 }
4866         }
4867
4868         // set the parent pointers
4869         Mod_Q1BSP_LoadNodes_RecursiveSetParent(loadmodel->brush.data_nodes, NULL);
4870 }
4871
4872 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
4873 {
4874         q3dlightgrid_t *in;
4875         q3dlightgrid_t *out;
4876         int count;
4877
4878         in = (q3dlightgrid_t *)(mod_base + l->fileofs);
4879         if (l->filelen % sizeof(*in))
4880                 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
4881         loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
4882         loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
4883         loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
4884         loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4885         loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4886         loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4887         loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4888         loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4889         loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4890         loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4891         loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4892         loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4893         count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4894         Matrix4x4_CreateScale3(&loadmodel->brushq3.num_lightgrid_indexfromworld, loadmodel->brushq3.num_lightgrid_scale[0], loadmodel->brushq3.num_lightgrid_scale[1], loadmodel->brushq3.num_lightgrid_scale[2]);
4895         Matrix4x4_ConcatTranslate(&loadmodel->brushq3.num_lightgrid_indexfromworld, -loadmodel->brushq3.num_lightgrid_imins[0] * loadmodel->brushq3.num_lightgrid_cellsize[0], -loadmodel->brushq3.num_lightgrid_imins[1] * loadmodel->brushq3.num_lightgrid_cellsize[1], -loadmodel->brushq3.num_lightgrid_imins[2] * loadmodel->brushq3.num_lightgrid_cellsize[2]);
4896
4897         // if lump is empty there is nothing to load, we can deal with that in the LightPoint code
4898         if (l->filelen)
4899         {
4900                 if (l->filelen < count * (int)sizeof(*in))
4901                         Host_Error("Mod_Q3BSP_LoadLightGrid: invalid lightgrid lump size %i bytes, should be %i bytes (%ix%ix%i)", l->filelen, count * sizeof(*in), loadmodel->brushq3.num_lightgrid_dimensions[0], loadmodel->brushq3.num_lightgrid_dimensions[1], loadmodel->brushq3.num_lightgrid_dimensions[2]);
4902                 if (l->filelen != count * (int)sizeof(*in))
4903                         Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i", count * sizeof(*in), l->filelen);
4904                 out = (q3dlightgrid_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4905                 loadmodel->brushq3.data_lightgrid = out;
4906                 loadmodel->brushq3.num_lightgrid = count;
4907                 // no swapping or validation necessary
4908                 memcpy(out, in, count * (int)sizeof(*out));
4909         }
4910 }
4911
4912 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4913 {
4914         q3dpvs_t *in;
4915         int totalchains;
4916
4917         if (l->filelen == 0)
4918         {
4919                 int i;
4920                 // unvised maps often have cluster indices even without pvs, so check
4921                 // leafs to find real number of clusters
4922                 loadmodel->brush.num_pvsclusters = 1;
4923                 for (i = 0;i < loadmodel->brush.num_leafs;i++)
4924                         loadmodel->brush.num_pvsclusters = max(loadmodel->brush.num_pvsclusters, loadmodel->brush.data_leafs[i].clusterindex + 1);
4925
4926                 // create clusters
4927                 loadmodel->brush.num_pvsclusterbytes = (loadmodel->brush.num_pvsclusters + 7) / 8;
4928                 totalchains = loadmodel->brush.num_pvsclusterbytes * loadmodel->brush.num_pvsclusters;
4929                 loadmodel->brush.data_pvsclusters = (unsigned char *)Mem_Alloc(loadmodel->mempool, totalchains);
4930                 memset(loadmodel->brush.data_pvsclusters, 0xFF, totalchains);
4931                 return;
4932         }
4933
4934         in = (q3dpvs_t *)(mod_base + l->fileofs);
4935         if (l->filelen < 9)
4936                 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4937
4938         loadmodel->brush.num_pvsclusters = LittleLong(in->numclusters);
4939         loadmodel->brush.num_pvsclusterbytes = LittleLong(in->chainlength);
4940         if (loadmodel->brush.num_pvsclusterbytes < ((loadmodel->brush.num_pvsclusters + 7) / 8))
4941                 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8", loadmodel->brush.num_pvsclusterbytes, loadmodel->brush.num_pvsclusters);
4942         totalchains = loadmodel->brush.num_pvsclusterbytes * loadmodel->brush.num_pvsclusters;
4943         if (l->filelen < totalchains + (int)sizeof(*in))
4944                 Host_Error("Mod_Q3BSP_LoadPVS: lump too small ((numclusters = %i) * (chainlength = %i) + sizeof(q3dpvs_t) == %i bytes, lump is %i bytes)", loadmodel->brush.num_pvsclusters, loadmodel->brush.num_pvsclusterbytes, totalchains + sizeof(*in), l->filelen);
4945
4946         loadmodel->brush.data_pvsclusters = (unsigned char *)Mem_Alloc(loadmodel->mempool, totalchains);
4947         memcpy(loadmodel->brush.data_pvsclusters, (unsigned char *)(in + 1), totalchains);
4948 }
4949
4950 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4951 {
4952         int i, j, k, index[3];
4953         float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4954         q3dlightgrid_t *a, *s;
4955         if (!model->brushq3.num_lightgrid)
4956         {
4957                 ambientcolor[0] = 1;
4958                 ambientcolor[1] = 1;
4959                 ambientcolor[2] = 1;
4960                 return;
4961         }
4962         Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4963         //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4964         //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4965         transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0] - 1);
4966         transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1] - 1);
4967         transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2] - 1);
4968         index[0] = (int)floor(transformed[0]);
4969         index[1] = (int)floor(transformed[1]);
4970         index[2] = (int)floor(transformed[2]);
4971         //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4972         // now lerp the values
4973         VectorClear(diffusenormal);
4974         a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4975         for (k = 0;k < 2;k++)
4976         {
4977                 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4978                 if (blend1 < 0.001f || index[2] + k >= model->brushq3.num_lightgrid_isize[2])
4979                         continue;
4980                 for (j = 0;j < 2;j++)
4981                 {
4982                         blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4983                         if (blend2 < 0.001f || index[1] + j >= model->brushq3.num_lightgrid_isize[1])
4984                                 continue;
4985                         for (i = 0;i < 2;i++)
4986                         {
4987                                 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4988                                 if (blend < 0.001f || index[0] + i >= model->brushq3.num_lightgrid_isize[0])
4989                                         continue;
4990                                 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4991                                 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4992                                 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4993                                 pitch = s->diffusepitch * M_PI / 128;
4994                                 yaw = s->diffuseyaw * M_PI / 128;
4995                                 sinpitch = sin(pitch);
4996                                 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4997                                 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4998                                 diffusenormal[2] += blend * (cos(pitch));
4999                                 //Con_Printf("blend %f: ambient %i %i %i, diffuse %i %i %i, diffusepitch %i diffuseyaw %i (%f %f, normal %f %f %f)\n", blend, s->ambientrgb[0], s->ambientrgb[1], s->ambientrgb[2], s->diffusergb[0], s->diffusergb[1], s->diffusergb[2], s->diffusepitch, s->diffuseyaw, pitch, yaw, (cos(yaw) * cospitch), (sin(yaw) * cospitch), (-sin(pitch)));
5000                         }
5001                 }
5002         }
5003         VectorNormalize(diffusenormal);
5004         //Con_Printf("result: ambient %f %f %f diffuse %f %f %f diffusenormal %f %f %f\n", ambientcolor[0], ambientcolor[1], ambientcolor[2], diffusecolor[0], diffusecolor[1], diffusecolor[2], diffusenormal[0], diffusenormal[1], diffusenormal[2]);
5005 }
5006
5007 static void Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace_t *trace, model_t *model, mnode_t *node, const vec3_t point, int markframe)
5008 {
5009         int i;
5010         mleaf_t *leaf;
5011         colbrushf_t *brush;
5012         // find which leaf the point is in
5013         while (node->plane)
5014                 node = node->children[DotProduct(point, node->plane->normal) < node->plane->dist];
5015         // point trace the brushes
5016         leaf = (mleaf_t *)node;
5017         for (i = 0;i < leaf->numleafbrushes;i++)
5018         {
5019                 brush = model->brush.data_brushes[leaf->firstleafbrush[i]].colbrushf;
5020                 if (brush && brush->markframe != markframe && BoxesOverlap(point, point, brush->mins, brush->maxs))
5021                 {
5022                         brush->markframe = markframe;
5023                         Collision_TracePointBrushFloat(trace, point, brush);
5024                 }
5025         }
5026         // can't do point traces on curves (they have no thickness)
5027 }
5028
5029 static void Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace_t *trace, model_t *model, mnode_t *node, const vec3_t start, const vec3_t end, vec_t startfrac, vec_t endfrac, const vec3_t linestart, const vec3_t lineend, int markframe, const vec3_t segmentmins, const vec3_t segmentmaxs)
5030 {
5031         int i, startside, endside;
5032         float dist1, dist2, midfrac, mid[3], nodesegmentmins[3], nodesegmentmaxs[3];
5033         mleaf_t *leaf;
5034         msurface_t *surface;
5035         mplane_t *plane;
5036         colbrushf_t *brush;
5037         // walk the tree until we hit a leaf, recursing for any split cases
5038         while (node->plane)
5039         {
5040                 plane = node->plane;
5041                 // axial planes are much more common than non-axial, so an optimized
5042                 // axial case pays off here
5043                 if (plane->type < 3)
5044                 {
5045                         dist1 = start[plane->type] - plane->dist;
5046                         dist2 = end[plane->type] - plane->dist;
5047                 }
5048                 else
5049                 {
5050                         dist1 = DotProduct(start, plane->normal) - plane->dist;
5051                         dist2 = DotProduct(end, plane->normal) - plane->dist;
5052                 }
5053                 startside = dist1 < 0;
5054                 endside = dist2 < 0;
5055                 if (startside == endside)
5056                 {
5057                         // most of the time the line fragment is on one side of the plane
5058                         node = node->children[startside];
5059                 }
5060                 else
5061                 {
5062                         // line crosses node plane, split the line
5063                         dist1 = PlaneDiff(linestart, plane);
5064                         dist2 = PlaneDiff(lineend, plane);
5065                         midfrac = dist1 / (dist1 - dist2);
5066                         VectorLerp(linestart, midfrac, lineend, mid);
5067                         // take the near side first
5068                         Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
5069                         // if we found an impact on the front side, don't waste time
5070                         // exploring the far side
5071                         if (midfrac <= trace->realfraction)
5072                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
5073                         return;
5074                 }
5075         }
5076         // hit a leaf
5077         nodesegmentmins[0] = min(start[0], end[0]) - 1;
5078         nodesegmentmins[1] = min(start[1], end[1]) - 1;
5079         nodesegmentmins[2] = min(start[2], end[2]) - 1;
5080         nodesegmentmaxs[0] = max(start[0], end[0]) + 1;
5081         nodesegmentmaxs[1] = max(start[1], end[1]) + 1;
5082         nodesegmentmaxs[2] = max(start[2], end[2]) + 1;
5083         // line trace the brushes
5084         leaf = (mleaf_t *)node;
5085         for (i = 0;i < leaf->numleafbrushes;i++)
5086         {
5087                 brush = model->brush.data_brushes[leaf->firstleafbrush[i]].colbrushf;
5088                 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
5089                 {
5090                         brush->markframe = markframe;
5091                         Collision_TraceLineBrushFloat(trace, linestart, lineend, brush, brush);
5092                 }
5093         }
5094         // can't do point traces on curves (they have no thickness)
5095         if (mod_q3bsp_curves_collisions.integer && !VectorCompare(start, end))
5096         {
5097                 // line trace the curves
5098                 for (i = 0;i < leaf->numleafsurfaces;i++)
5099                 {
5100                         surface = model->data_surfaces + leaf->firstleafsurface[i];
5101                         if (surface->num_collisiontriangles && surface->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, surface->mins, surface->maxs))
5102                         {
5103                                 surface->collisionmarkframe = markframe;
5104                                 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, segmentmins, segmentmaxs);
5105                         }
5106                 }
5107         }
5108 }
5109
5110 static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, model_t *model, mnode_t *node, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int markframe, const vec3_t segmentmins, const vec3_t segmentmaxs)
5111 {
5112         int i;
5113         int sides;
5114         mleaf_t *leaf;
5115         colbrushf_t *brush;
5116         msurface_t *surface;
5117         mplane_t *plane;
5118         float nodesegmentmins[3], nodesegmentmaxs[3];
5119         // walk the tree until we hit a leaf, recursing for any split cases
5120         while (node->plane)
5121         {
5122                 plane = node->plane;
5123                 // axial planes are much more common than non-axial, so an optimized
5124                 // axial case pays off here
5125                 if (plane->type < 3)
5126                 {
5127                         // this is an axial plane, compare bounding box directly to it and
5128                         // recurse sides accordingly
5129                         // recurse down node sides
5130                         // use an inlined axial BoxOnPlaneSide to slightly reduce overhead
5131                         //sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, plane);
5132                         //sides = ((segmentmaxs[plane->type] >= plane->dist) | ((segmentmins[plane->type] < plane->dist) << 1));
5133                         sides = ((segmentmaxs[plane->type] >= plane->dist) + ((segmentmins[plane->type] < plane->dist) * 2));
5134                 }
5135                 else
5136                 {
5137                         // this is a non-axial plane, so check if the start and end boxes
5138                         // are both on one side of the plane to handle 'diagonal' cases
5139                         sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, plane);
5140                 }
5141                 if (sides == 3)
5142                 {
5143                         // segment crosses plane
5144                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5145                         sides = 2;
5146                 }
5147                 // if sides == 0 then the trace itself is bogus (Not A Number values),
5148                 // in this case we simply pretend the trace hit nothing
5149                 if (sides == 0)
5150                         return; // ERROR: NAN bounding box!
5151                 // take whichever side the segment box is on
5152                 node = node->children[sides - 1];
5153         }
5154         nodesegmentmins[0] = max(segmentmins[0], node->mins[0] - 1);
5155         nodesegmentmins[1] = max(segmentmins[1], node->mins[1] - 1);
5156         nodesegmentmins[2] = max(segmentmins[2], node->mins[2] - 1);
5157         nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0] + 1);
5158         nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1] + 1);
5159         nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2] + 1);
5160         // hit a leaf
5161         leaf = (mleaf_t *)node;
5162         for (i = 0;i < leaf->numleafbrushes;i++)
5163         {
5164                 brush = model->brush.data_brushes[leaf->firstleafbrush[i]].colbrushf;
5165                 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
5166                 {
5167                         brush->markframe = markframe;
5168                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, brush, brush);
5169                 }
5170         }
5171         if (mod_q3bsp_curves_collisions.integer)
5172         {
5173                 for (i = 0;i < leaf->numleafsurfaces;i++)
5174                 {
5175                         surface = model->data_surfaces + leaf->firstleafsurface[i];
5176                         if (surface->num_collisiontriangles && surface->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, surface->mins, surface->maxs))
5177                         {
5178                                 surface->collisionmarkframe = markframe;
5179                                 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, segmentmins, segmentmaxs);
5180                         }
5181                 }
5182         }
5183 }
5184
5185 static void Mod_Q3BSP_TraceBox(model_t *model, int frame, trace_t *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask)
5186 {
5187         int i;
5188         float segmentmins[3], segmentmaxs[3];
5189         static int markframe = 0;
5190         msurface_t *surface;
5191         q3mbrush_t *brush;
5192         memset(trace, 0, sizeof(*trace));
5193         trace->fraction = 1;
5194         trace->realfraction = 1;
5195         trace->hitsupercontentsmask = hitsupercontentsmask;
5196         if (mod_q3bsp_optimizedtraceline.integer && VectorLength2(boxmins) + VectorLength2(boxmaxs) == 0)
5197         {
5198                 if (VectorCompare(start, end))
5199                 {
5200                         // point trace
5201                         if (model->brush.submodel)
5202                         {
5203                                 for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
5204                                         if (brush->colbrushf)
5205                                                 Collision_TracePointBrushFloat(trace, start, brush->colbrushf);
5206                         }
5207                         else
5208                                 Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace, model, model->brush.data_nodes, start, ++markframe);
5209                 }
5210                 else
5211                 {
5212                         // line trace
5213                         segmentmins[0] = min(start[0], end[0]) - 1;
5214                         segmentmins[1] = min(start[1], end[1]) - 1;
5215                         segmentmins[2] = min(start[2], end[2]) - 1;
5216                         segmentmaxs[0] = max(start[0], end[0]) + 1;
5217                         segmentmaxs[1] = max(start[1], end[1]) + 1;
5218                         segmentmaxs[2] = max(start[2], end[2]) + 1;
5219                         if (model->brush.submodel)
5220                         {
5221                                 for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
5222                                         if (brush->colbrushf)
5223                                                 Collision_TraceLineBrushFloat(trace, start, end, brush->colbrushf, brush->colbrushf);
5224                                 if (mod_q3bsp_curves_collisions.integer)
5225                                         for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
5226                                                 if (surface->num_collisiontriangles)
5227                                                         Collision_TraceLineTriangleMeshFloat(trace, start, end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, segmentmins, segmentmaxs);
5228                         }
5229                         else
5230                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, model->brush.data_nodes, start, end, 0, 1, start, end, ++markframe, segmentmins, segmentmaxs);
5231                 }
5232         }
5233         else
5234         {
5235                 // box trace, performed as brush trace
5236                 colbrushf_t *thisbrush_start, *thisbrush_end;
5237                 vec3_t boxstartmins, boxstartmaxs, boxendmins, boxendmaxs;
5238                 segmentmins[0] = min(start[0], end[0]) + boxmins[0] - 1;
5239                 segmentmins[1] = min(start[1], end[1]) + boxmins[1] - 1;
5240                 segmentmins[2] = min(start[2], end[2]) + boxmins[2] - 1;
5241                 segmentmaxs[0] = max(start[0], end[0]) + boxmaxs[0] + 1;
5242                 segmentmaxs[1] = max(start[1], end[1]) + boxmaxs[1] + 1;
5243                 segmentmaxs[2] = max(start[2], end[2]) + boxmaxs[2] + 1;
5244                 VectorAdd(start, boxmins, boxstartmins);
5245                 VectorAdd(start, boxmaxs, boxstartmaxs);
5246                 VectorAdd(end, boxmins, boxendmins);
5247                 VectorAdd(end, boxmaxs, boxendmaxs);
5248                 thisbrush_start = Collision_BrushForBox(&identitymatrix, boxstartmins, boxstartmaxs);
5249                 thisbrush_end = Collision_BrushForBox(&identitymatrix, boxendmins, boxendmaxs);
5250                 if (model->brush.submodel)
5251                 {
5252                         for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
5253                                 if (brush->colbrushf)
5254                                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, brush->colbrushf, brush->colbrushf);
5255                         if (mod_q3bsp_curves_collisions.integer)
5256                                 for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
5257                                         if (surface->num_collisiontriangles)
5258                                                 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, segmentmins, segmentmaxs);
5259                 }
5260                 else
5261                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, model->brush.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
5262         }
5263 }
5264
5265 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
5266 {
5267         int supercontents = 0;
5268         if (nativecontents & CONTENTSQ3_SOLID)
5269                 supercontents |= SUPERCONTENTS_SOLID;
5270         if (nativecontents & CONTENTSQ3_WATER)
5271                 supercontents |= SUPERCONTENTS_WATER;
5272         if (nativecontents & CONTENTSQ3_SLIME)
5273                 supercontents |= SUPERCONTENTS_SLIME;
5274         if (nativecontents & CONTENTSQ3_LAVA)
5275                 supercontents |= SUPERCONTENTS_LAVA;
5276         if (nativecontents & CONTENTSQ3_BODY)
5277                 supercontents |= SUPERCONTENTS_BODY;
5278         if (nativecontents & CONTENTSQ3_CORPSE)
5279                 supercontents |= SUPERCONTENTS_CORPSE;
5280         if (nativecontents & CONTENTSQ3_NODROP)
5281                 supercontents |= SUPERCONTENTS_NODROP;
5282         if (nativecontents & CONTENTSQ3_PLAYERCLIP)
5283                 supercontents |= SUPERCONTENTS_PLAYERCLIP;
5284         if (nativecontents & CONTENTSQ3_MONSTERCLIP)
5285                 supercontents |= SUPERCONTENTS_MONSTERCLIP;
5286         if (nativecontents & CONTENTSQ3_DONOTENTER)
5287                 supercontents |= SUPERCONTENTS_DONOTENTER;
5288         return supercontents;
5289 }
5290
5291 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
5292 {
5293         int nativecontents = 0;
5294         if (supercontents & SUPERCONTENTS_SOLID)
5295                 nativecontents |= CONTENTSQ3_SOLID;
5296         if (supercontents & SUPERCONTENTS_WATER)
5297                 nativecontents |= CONTENTSQ3_WATER;
5298         if (supercontents & SUPERCONTENTS_SLIME)
5299                 nativecontents |= CONTENTSQ3_SLIME;
5300         if (supercontents & SUPERCONTENTS_LAVA)
5301                 nativecontents |= CONTENTSQ3_LAVA;
5302         if (supercontents & SUPERCONTENTS_BODY)
5303                 nativecontents |= CONTENTSQ3_BODY;
5304         if (supercontents & SUPERCONTENTS_CORPSE)
5305                 nativecontents |= CONTENTSQ3_CORPSE;
5306         if (supercontents & SUPERCONTENTS_NODROP)
5307                 nativecontents |= CONTENTSQ3_NODROP;
5308         if (supercontents & SUPERCONTENTS_PLAYERCLIP)
5309                 nativecontents |= CONTENTSQ3_PLAYERCLIP;
5310         if (supercontents & SUPERCONTENTS_MONSTERCLIP)
5311                 nativecontents |= CONTENTSQ3_MONSTERCLIP;
5312         if (supercontents & SUPERCONTENTS_DONOTENTER)
5313                 nativecontents |= CONTENTSQ3_DONOTENTER;
5314         return nativecontents;
5315 }
5316
5317 void Mod_Q3BSP_RecursiveFindNumLeafs(mnode_t *node)
5318 {
5319         int numleafs;
5320         while (node->plane)
5321         {
5322                 Mod_Q3BSP_RecursiveFindNumLeafs(node->children[0]);
5323                 node = node->children[1];
5324         }
5325         numleafs = ((mleaf_t *)node - loadmodel->brush.data_leafs) + 1;
5326         if (loadmodel->brush.num_leafs < numleafs)
5327                 loadmodel->brush.num_leafs = numleafs;
5328 }
5329
5330 void Mod_Q3BSP_Load(model_t *mod, void *buffer, void *bufferend)
5331 {
5332         int i, j, numshadowmeshtriangles;
5333         q3dheader_t *header;
5334         float corner[3], yawradius, modelradius;
5335         msurface_t *surface;
5336
5337         mod->type = mod_brushq3;
5338         mod->numframes = 2; // although alternate textures are not supported it is annoying to complain about no such frame 1
5339         mod->numskins = 1;
5340
5341         header = (q3dheader_t *)buffer;
5342
5343         i = LittleLong(header->version);
5344         if (i != Q3BSPVERSION)
5345                 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
5346         mod->brush.ishlbsp = false;
5347         mod->brush.ismcbsp = false;
5348         if (loadmodel->isworldmodel)
5349         {
5350                 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
5351                 Cvar_SetValue("mcbsp", mod->brush.ismcbsp);
5352         }
5353
5354         mod->soundfromcenter = true;
5355         mod->TraceBox = Mod_Q3BSP_TraceBox;
5356         mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
5357         mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
5358         mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
5359         mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
5360         mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
5361         mod->brush.BoxTouchingLeafPVS = Mod_Q1BSP_BoxTouchingLeafPVS;
5362         mod->brush.BoxTouchingVisibleLeafs = Mod_Q1BSP_BoxTouchingVisibleLeafs;
5363         mod->brush.FindBoxClusters = Mod_Q1BSP_FindBoxClusters;
5364         mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
5365         mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
5366         mod->brush.PointInLeaf = Mod_Q1BSP_PointInLeaf;
5367         mod->Draw = R_Q1BSP_Draw;
5368         mod->GetLightInfo = R_Q1BSP_GetLightInfo;
5369         mod->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
5370         mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
5371         mod->DrawLight = R_Q1BSP_DrawLight;
5372
5373         mod_base = (unsigned char *)header;
5374
5375         // swap all the lumps
5376         header->ident = LittleLong(header->ident);
5377         header->version = LittleLong(header->version);
5378         for (i = 0;i < Q3HEADER_LUMPS;i++)
5379         {
5380                 header->lumps[i].fileofs = LittleLong(header->lumps[i].fileofs);
5381                 header->lumps[i].filelen = LittleLong(header->lumps[i].filelen);
5382         }
5383
5384         mod->brush.qw_md4sum = 0;
5385         mod->brush.qw_md4sum2 = 0;
5386         for (i = 0;i < Q3HEADER_LUMPS;i++)
5387         {
5388                 if (i == Q3LUMP_ENTITIES)
5389                         continue;
5390                 mod->brush.qw_md4sum ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen);
5391                 if (i == Q3LUMP_PVS || i == Q3LUMP_LEAFS || i == Q3LUMP_NODES)
5392                         continue;
5393                 mod->brush.qw_md4sum2 ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen);
5394         }
5395
5396         Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
5397         Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
5398         Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
5399         Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
5400         Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
5401         Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
5402         Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
5403         Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
5404         Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
5405         Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
5406         Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
5407         Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
5408         Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
5409         Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
5410         Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
5411         Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
5412         Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
5413         loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
5414
5415         // the MakePortals code works fine on the q3bsp data as well
5416         Mod_Q1BSP_MakePortals();
5417
5418         // make a single combined shadow mesh to allow optimized shadow volume creation
5419         numshadowmeshtriangles = 0;
5420         for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
5421         {
5422                 surface->num_firstshadowmeshtriangle = numshadowmeshtriangles;
5423                 numshadowmeshtriangles += surface->num_triangles;
5424         }
5425         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
5426         for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
5427                 if (surface->groupmesh)
5428                         Mod_ShadowMesh_AddMesh(loadmodel->mempool, loadmodel->brush.shadowmesh, NULL, NULL, NULL, surface->groupmesh->data_vertex3f, NULL, NULL, NULL, NULL, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
5429         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true);
5430         Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
5431
5432         loadmodel->brush.num_leafs = 0;
5433         Mod_Q3BSP_RecursiveFindNumLeafs(loadmodel->brush.data_nodes);
5434
5435         if (loadmodel->isworldmodel)
5436         {
5437                 // clear out any stale submodels or worldmodels lying around
5438                 // if we did this clear before now, an error might abort loading and
5439                 // leave things in a bad state
5440                 Mod_RemoveStaleWorldModels(loadmodel);
5441         }
5442
5443         mod = loadmodel;
5444         for (i = 0;i < loadmodel->brush.numsubmodels;i++)
5445         {
5446                 if (i > 0)
5447                 {
5448                         char name[10];
5449                         // LordHavoc: only register submodels if it is the world
5450                         // (prevents external bsp models from replacing world submodels with
5451                         //  their own)
5452                         if (!loadmodel->isworldmodel)
5453                                 continue;
5454                         // duplicate the basic information
5455                         sprintf(name, "*%i", i);
5456                         mod = Mod_FindName(name);
5457                         *mod = *loadmodel;
5458                         strcpy(mod->name, name);
5459                         // textures and memory belong to the main model
5460                         mod->texturepool = NULL;
5461                         mod->mempool = NULL;
5462                         mod->brush.GetPVS = NULL;
5463                         mod->brush.FatPVS = NULL;
5464                         mod->brush.BoxTouchingPVS = NULL;
5465                         mod->brush.BoxTouchingLeafPVS = NULL;
5466                         mod->brush.BoxTouchingVisibleLeafs = NULL;
5467                         mod->brush.FindBoxClusters = NULL;
5468                         mod->brush.LightPoint = NULL;
5469                         mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
5470                 }
5471                 mod->brush.submodel = i;
5472
5473                 // make the model surface list (used by shadowing/lighting)
5474                 mod->firstmodelsurface = mod->brushq3.data_models[i].firstface;
5475                 mod->nummodelsurfaces = mod->brushq3.data_models[i].numfaces;
5476                 mod->firstmodelbrush = mod->brushq3.data_models[i].firstbrush;
5477                 mod->nummodelbrushes = mod->brushq3.data_models[i].numbrushes;
5478                 mod->surfacelist = (int *)Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->surfacelist));
5479                 for (j = 0;j < mod->nummodelsurfaces;j++)
5480                         mod->surfacelist[j] = mod->firstmodelsurface + j;
5481
5482                 VectorCopy(mod->brushq3.data_models[i].mins, mod->normalmins);
5483                 VectorCopy(mod->brushq3.data_models[i].maxs, mod->normalmaxs);
5484                 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
5485                 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
5486                 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
5487                 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
5488                 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
5489                 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
5490                 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
5491                 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
5492                 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
5493                 mod->yawmins[2] = mod->normalmins[2];
5494                 mod->yawmaxs[2] = mod->normalmaxs[2];
5495                 mod->radius = modelradius;
5496                 mod->radius2 = modelradius * modelradius;
5497
5498                 for (j = 0;j < mod->nummodelsurfaces;j++)
5499                         if (mod->data_surfaces[j + mod->firstmodelsurface].texture->surfaceflags & Q3SURFACEFLAG_SKY)
5500                                 break;
5501                 if (j < mod->nummodelsurfaces)
5502                         mod->DrawSky = R_Q1BSP_DrawSky;
5503         }
5504 }
5505
5506 void Mod_IBSP_Load(model_t *mod, void *buffer, void *bufferend)
5507 {
5508         int i = LittleLong(((int *)buffer)[1]);
5509         if (i == Q3BSPVERSION)
5510                 Mod_Q3BSP_Load(mod,buffer, bufferend);
5511         else if (i == Q2BSPVERSION)
5512                 Mod_Q2BSP_Load(mod,buffer, bufferend);
5513         else
5514                 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i", i);
5515 }
5516
5517 void Mod_MAP_Load(model_t *mod, void *buffer, void *bufferend)
5518 {
5519         Host_Error("Mod_MAP_Load: not yet implemented");
5520 }
5521