]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - model_brush.c
some cleanup of surface->cached_dlight handling to fix the broken unlit q1bsp handlin...
[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 boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, 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(boxstartmaxs, boxstartmins, 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         VectorSubtract(boxstartmins, rhc.hull->clip_mins, rhc.start);
807         VectorSubtract(boxendmins, 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 (DotProduct(rhc.dist, 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 }
1056
1057 static void Mod_Q1BSP_DecompressVis(const unsigned char *in, const unsigned char *inend, unsigned char *out, unsigned char *outend)
1058 {
1059         int c;
1060         unsigned char *outstart = out;
1061         while (out < outend)
1062         {
1063                 if (in == inend)
1064                 {
1065                         Con_Printf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
1066                         return;
1067                 }
1068                 c = *in++;
1069                 if (c)
1070                         *out++ = c;
1071                 else
1072                 {
1073                         if (in == inend)
1074                         {
1075                                 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);
1076                                 return;
1077                         }
1078                         for (c = *in++;c > 0;c--)
1079                         {
1080                                 if (out == outend)
1081                                 {
1082                                         Con_Printf("Mod_Q1BSP_DecompressVis: output overrun on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
1083                                         return;
1084                                 }
1085                                 *out++ = 0;
1086                         }
1087                 }
1088         }
1089 }
1090
1091 /*
1092 =============
1093 R_Q1BSP_LoadSplitSky
1094
1095 A sky texture is 256*128, with the right side being a masked overlay
1096 ==============
1097 */
1098 void R_Q1BSP_LoadSplitSky (unsigned char *src, int width, int height, int bytesperpixel)
1099 {
1100         int i, j;
1101         unsigned solidpixels[128*128], alphapixels[128*128];
1102
1103         // if sky isn't the right size, just use it as a solid layer
1104         if (width != 256 || height != 128)
1105         {
1106                 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);
1107                 loadmodel->brush.alphaskytexture = NULL;
1108                 return;
1109         }
1110
1111         if (bytesperpixel == 4)
1112         {
1113                 for (i = 0;i < 128;i++)
1114                 {
1115                         for (j = 0;j < 128;j++)
1116                         {
1117                                 solidpixels[(i*128) + j] = ((unsigned *)src)[i*256+j+128];
1118                                 alphapixels[(i*128) + j] = ((unsigned *)src)[i*256+j];
1119                         }
1120                 }
1121         }
1122         else
1123         {
1124                 // make an average value for the back to avoid
1125                 // a fringe on the top level
1126                 int p, r, g, b;
1127                 union
1128                 {
1129                         unsigned int i;
1130                         unsigned char b[4];
1131                 }
1132                 rgba;
1133                 r = g = b = 0;
1134                 for (i = 0;i < 128;i++)
1135                 {
1136                         for (j = 0;j < 128;j++)
1137                         {
1138                                 rgba.i = palette_complete[src[i*256 + j + 128]];
1139                                 r += rgba.b[0];
1140                                 g += rgba.b[1];
1141                                 b += rgba.b[2];
1142                         }
1143                 }
1144                 rgba.b[0] = r/(128*128);
1145                 rgba.b[1] = g/(128*128);
1146                 rgba.b[2] = b/(128*128);
1147                 rgba.b[3] = 0;
1148                 for (i = 0;i < 128;i++)
1149                 {
1150                         for (j = 0;j < 128;j++)
1151                         {
1152                                 solidpixels[(i*128) + j] = palette_complete[src[i*256 + j + 128]];
1153                                 alphapixels[(i*128) + j] = (p = src[i*256 + j]) ? palette_complete[p] : rgba.i;
1154                         }
1155                 }
1156         }
1157
1158         loadmodel->brush.solidskytexture = R_LoadTexture2D(loadmodel->texturepool, "sky_solidtexture", 128, 128, (unsigned char *) solidpixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1159         loadmodel->brush.alphaskytexture = R_LoadTexture2D(loadmodel->texturepool, "sky_alphatexture", 128, 128, (unsigned char *) alphapixels, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1160 }
1161
1162 static void Mod_Q1BSP_LoadTextures(lump_t *l)
1163 {
1164         int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
1165         miptex_t *dmiptex;
1166         texture_t *tx, *tx2, *anims[10], *altanims[10];
1167         dmiptexlump_t *m;
1168         unsigned char *data, *mtdata;
1169         char name[MAX_QPATH];
1170
1171         loadmodel->data_textures = NULL;
1172
1173         // add two slots for notexture walls and notexture liquids
1174         if (l->filelen)
1175         {
1176                 m = (dmiptexlump_t *)(mod_base + l->fileofs);
1177                 m->nummiptex = LittleLong (m->nummiptex);
1178                 loadmodel->num_textures = m->nummiptex + 2;
1179         }
1180         else
1181         {
1182                 m = NULL;
1183                 loadmodel->num_textures = 2;
1184         }
1185
1186         loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_textures * sizeof(texture_t));
1187
1188         // fill out all slots with notexture
1189         for (i = 0, tx = loadmodel->data_textures;i < loadmodel->num_textures;i++, tx++)
1190         {
1191                 strcpy(tx->name, "NO TEXTURE FOUND");
1192                 tx->width = 16;
1193                 tx->height = 16;
1194                 tx->skin.base = r_texture_notexture;
1195                 tx->basematerialflags = 0;
1196                 if (i == loadmodel->num_textures - 1)
1197                 {
1198                         tx->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_LIGHTBOTHSIDES;
1199                         tx->supercontents = SUPERCONTENTS_WATER;
1200                 }
1201                 else
1202                 {
1203                         tx->basematerialflags |= MATERIALFLAG_WALL;
1204                         tx->supercontents = SUPERCONTENTS_SOLID;
1205                 }
1206                 tx->currentframe = tx;
1207         }
1208
1209         if (!m)
1210                 return;
1211
1212         // just to work around bounds checking when debugging with it (array index out of bounds error thing)
1213         dofs = m->dataofs;
1214         // LordHavoc: mostly rewritten map texture loader
1215         for (i = 0;i < m->nummiptex;i++)
1216         {
1217                 dofs[i] = LittleLong(dofs[i]);
1218                 if (dofs[i] == -1 || r_nosurftextures.integer)
1219                         continue;
1220                 dmiptex = (miptex_t *)((unsigned char *)m + dofs[i]);
1221
1222                 // make sure name is no more than 15 characters
1223                 for (j = 0;dmiptex->name[j] && j < 15;j++)
1224                         name[j] = dmiptex->name[j];
1225                 name[j] = 0;
1226
1227                 mtwidth = LittleLong(dmiptex->width);
1228                 mtheight = LittleLong(dmiptex->height);
1229                 mtdata = NULL;
1230                 j = LittleLong(dmiptex->offsets[0]);
1231                 if (j)
1232                 {
1233                         // texture included
1234                         if (j < 40 || j + mtwidth * mtheight > l->filelen)
1235                         {
1236                                 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
1237                                 continue;
1238                         }
1239                         mtdata = (unsigned char *)dmiptex + j;
1240                 }
1241
1242                 if ((mtwidth & 15) || (mtheight & 15))
1243                         Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned\n", dmiptex->name, loadmodel->name);
1244
1245                 // LordHavoc: force all names to lowercase
1246                 for (j = 0;name[j];j++)
1247                         if (name[j] >= 'A' && name[j] <= 'Z')
1248                                 name[j] += 'a' - 'A';
1249
1250                 tx = loadmodel->data_textures + i;
1251                 strcpy(tx->name, name);
1252                 tx->width = mtwidth;
1253                 tx->height = mtheight;
1254
1255                 if (!tx->name[0])
1256                 {
1257                         sprintf(tx->name, "unnamed%i", i);
1258                         Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
1259                 }
1260
1261                 if (cls.state != ca_dedicated)
1262                 {
1263                         // LordHavoc: HL sky textures are entirely different than quake
1264                         if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
1265                         {
1266                                 if (loadmodel->isworldmodel)
1267                                 {
1268                                         data = loadimagepixels(tx->name, false, 0, 0);
1269                                         if (data)
1270                                         {
1271                                                 R_Q1BSP_LoadSplitSky(data, image_width, image_height, 4);
1272                                                 Mem_Free(data);
1273                                         }
1274                                         else if (mtdata != NULL)
1275                                                 R_Q1BSP_LoadSplitSky(mtdata, mtwidth, mtheight, 1);
1276                                 }
1277                         }
1278                         else
1279                         {
1280                                 if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, true))
1281                                 {
1282                                         // did not find external texture, load it from the bsp or wad3
1283                                         if (loadmodel->brush.ishlbsp)
1284                                         {
1285                                                 // internal texture overrides wad
1286                                                 unsigned char *pixels, *freepixels;
1287                                                 pixels = freepixels = NULL;
1288                                                 if (mtdata)
1289                                                         pixels = W_ConvertWAD3Texture(dmiptex);
1290                                                 if (pixels == NULL)
1291                                                         pixels = freepixels = W_GetTexture(tx->name);
1292                                                 if (pixels != NULL)
1293                                                 {
1294                                                         tx->width = image_width;
1295                                                         tx->height = image_height;
1296                                                         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);
1297                                                 }
1298                                                 if (freepixels)
1299                                                         Mem_Free(freepixels);
1300                                         }
1301                                         else if (mtdata) // texture included
1302                                                 Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE | TEXF_PICMIP, false, tx->name[0] != '*' && r_fullbrights.integer, mtdata, tx->width, tx->height, 8, NULL, NULL);
1303                                 }
1304                         }
1305                         if (tx->skin.base == NULL)
1306                         {
1307                                 // no texture found
1308                                 tx->width = 16;
1309                                 tx->height = 16;
1310                                 tx->skin.base = r_texture_notexture;
1311                         }
1312                 }
1313
1314                 tx->basematerialflags = 0;
1315                 if (tx->name[0] == '*')
1316                 {
1317                         // turb does not block movement
1318                         tx->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_LIGHTBOTHSIDES;
1319                         // LordHavoc: some turbulent textures should be fullbright and solid
1320                         if (!strncmp(tx->name,"*lava",5)
1321                          || !strncmp(tx->name,"*teleport",9)
1322                          || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
1323                                 tx->basematerialflags |= MATERIALFLAG_FULLBRIGHT;
1324                         else
1325                                 tx->basematerialflags |= MATERIALFLAG_WATERALPHA;
1326                         if (!strncmp(tx->name, "*lava", 5))
1327                                 tx->supercontents = SUPERCONTENTS_LAVA;
1328                         else if (!strncmp(tx->name, "*slime", 6))
1329                                 tx->supercontents = SUPERCONTENTS_SLIME;
1330                         else
1331                                 tx->supercontents = SUPERCONTENTS_WATER;
1332                 }
1333                 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
1334                 {
1335                         tx->supercontents = SUPERCONTENTS_SKY;
1336                         tx->basematerialflags |= MATERIALFLAG_SKY;
1337                 }
1338                 else
1339                 {
1340                         tx->supercontents = SUPERCONTENTS_SOLID;
1341                         tx->basematerialflags |= MATERIALFLAG_WALL;
1342                 }
1343                 if (tx->skin.fog)
1344                         tx->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_TRANSPARENT;
1345
1346                 // start out with no animation
1347                 tx->currentframe = tx;
1348         }
1349
1350         // sequence the animations
1351         for (i = 0;i < m->nummiptex;i++)
1352         {
1353                 tx = loadmodel->data_textures + i;
1354                 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
1355                         continue;
1356                 if (tx->anim_total[0] || tx->anim_total[1])
1357                         continue;       // already sequenced
1358
1359                 // find the number of frames in the animation
1360                 memset(anims, 0, sizeof(anims));
1361                 memset(altanims, 0, sizeof(altanims));
1362
1363                 for (j = i;j < m->nummiptex;j++)
1364                 {
1365                         tx2 = loadmodel->data_textures + j;
1366                         if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
1367                                 continue;
1368
1369                         num = tx2->name[1];
1370                         if (num >= '0' && num <= '9')
1371                                 anims[num - '0'] = tx2;
1372                         else if (num >= 'a' && num <= 'j')
1373                                 altanims[num - 'a'] = tx2;
1374                         else
1375                                 Con_Printf("Bad animating texture %s\n", tx->name);
1376                 }
1377
1378                 max = altmax = 0;
1379                 for (j = 0;j < 10;j++)
1380                 {
1381                         if (anims[j])
1382                                 max = j + 1;
1383                         if (altanims[j])
1384                                 altmax = j + 1;
1385                 }
1386                 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
1387
1388                 incomplete = false;
1389                 for (j = 0;j < max;j++)
1390                 {
1391                         if (!anims[j])
1392                         {
1393                                 Con_Printf("Missing frame %i of %s\n", j, tx->name);
1394                                 incomplete = true;
1395                         }
1396                 }
1397                 for (j = 0;j < altmax;j++)
1398                 {
1399                         if (!altanims[j])
1400                         {
1401                                 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
1402                                 incomplete = true;
1403                         }
1404                 }
1405                 if (incomplete)
1406                         continue;
1407
1408                 if (altmax < 1)
1409                 {
1410                         // if there is no alternate animation, duplicate the primary
1411                         // animation into the alternate
1412                         altmax = max;
1413                         for (k = 0;k < 10;k++)
1414                                 altanims[k] = anims[k];
1415                 }
1416
1417                 // link together the primary animation
1418                 for (j = 0;j < max;j++)
1419                 {
1420                         tx2 = anims[j];
1421                         tx2->animated = true;
1422                         tx2->anim_total[0] = max;
1423                         tx2->anim_total[1] = altmax;
1424                         for (k = 0;k < 10;k++)
1425                         {
1426                                 tx2->anim_frames[0][k] = anims[k];
1427                                 tx2->anim_frames[1][k] = altanims[k];
1428                         }
1429                 }
1430
1431                 // if there really is an alternate anim...
1432                 if (anims[0] != altanims[0])
1433                 {
1434                         // link together the alternate animation
1435                         for (j = 0;j < altmax;j++)
1436                         {
1437                                 tx2 = altanims[j];
1438                                 tx2->animated = true;
1439                                 // the primary/alternate are reversed here
1440                                 tx2->anim_total[0] = altmax;
1441                                 tx2->anim_total[1] = max;
1442                                 for (k = 0;k < 10;k++)
1443                                 {
1444                                         tx2->anim_frames[0][k] = altanims[k];
1445                                         tx2->anim_frames[1][k] = anims[k];
1446                                 }
1447                         }
1448                 }
1449         }
1450 }
1451
1452 static void Mod_Q1BSP_LoadLighting(lump_t *l)
1453 {
1454         int i;
1455         unsigned char *in, *out, *data, d;
1456         char litfilename[1024];
1457         fs_offset_t filesize;
1458         loadmodel->brushq1.lightdata = NULL;
1459         if (loadmodel->brush.ishlbsp) // LordHavoc: load the colored lighting data straight
1460         {
1461                 loadmodel->brushq1.lightdata = (unsigned char *)Mem_Alloc(loadmodel->mempool, l->filelen);
1462                 for (i=0; i<l->filelen; i++)
1463                         loadmodel->brushq1.lightdata[i] = mod_base[l->fileofs+i] >>= 1;
1464         }
1465         else if (loadmodel->brush.ismcbsp)
1466         {
1467                 loadmodel->brushq1.lightdata = (unsigned char *)Mem_Alloc(loadmodel->mempool, l->filelen);
1468                 memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen);
1469         }
1470         else // LordHavoc: bsp version 29 (normal white lighting)
1471         {
1472                 // LordHavoc: hope is not lost yet, check for a .lit file to load
1473                 strlcpy (litfilename, loadmodel->name, sizeof (litfilename));
1474                 FS_StripExtension (litfilename, litfilename, sizeof (litfilename));
1475                 strlcat (litfilename, ".lit", sizeof (litfilename));
1476                 data = (unsigned char*) FS_LoadFile(litfilename, tempmempool, false, &filesize);
1477                 if (data)
1478                 {
1479                         if (filesize == (fs_offset_t)(8 + l->filelen * 3) && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
1480                         {
1481                                 i = LittleLong(((int *)data)[1]);
1482                                 if (i == 1)
1483                                 {
1484                                         Con_DPrintf("loaded %s\n", litfilename);
1485                                         loadmodel->brushq1.lightdata = (unsigned char *)Mem_Alloc(loadmodel->mempool, filesize - 8);
1486                                         memcpy(loadmodel->brushq1.lightdata, data + 8, filesize - 8);
1487                                         Mem_Free(data);
1488                                         return;
1489                                 }
1490                                 else
1491                                 {
1492                                         Con_Printf("Unknown .lit file version (%d)\n", i);
1493                                         Mem_Free(data);
1494                                 }
1495                         }
1496                         else
1497                         {
1498                                 if (filesize == 8)
1499                                         Con_Print("Empty .lit file, ignoring\n");
1500                                 else
1501                                         Con_Printf("Corrupt .lit file (file size %i bytes, should be %i bytes), ignoring\n", filesize, 8 + l->filelen * 3);
1502                                 Mem_Free(data);
1503                         }
1504                 }
1505                 // LordHavoc: oh well, expand the white lighting data
1506                 if (!l->filelen)
1507                         return;
1508                 loadmodel->brushq1.lightdata = (unsigned char *)Mem_Alloc(loadmodel->mempool, l->filelen*3);
1509                 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
1510                 out = loadmodel->brushq1.lightdata;
1511                 memcpy(in, mod_base + l->fileofs, l->filelen);
1512                 for (i = 0;i < l->filelen;i++)
1513                 {
1514                         d = *in++;
1515                         *out++ = d;
1516                         *out++ = d;
1517                         *out++ = d;
1518                 }
1519         }
1520 }
1521
1522 static void Mod_Q1BSP_LoadLightList(void)
1523 {
1524         int a, n, numlights;
1525         char tempchar, *s, *t, *lightsstring, lightsfilename[1024];
1526         mlight_t *e;
1527
1528         strlcpy (lightsfilename, loadmodel->name, sizeof (lightsfilename));
1529         FS_StripExtension (lightsfilename, lightsfilename, sizeof(lightsfilename));
1530         strlcat (lightsfilename, ".lights", sizeof (lightsfilename));
1531         s = lightsstring = (char *) FS_LoadFile(lightsfilename, tempmempool, false, NULL);
1532         if (s)
1533         {
1534                 numlights = 0;
1535                 while (*s)
1536                 {
1537                         while (*s && *s != '\n' && *s != '\r')
1538                                 s++;
1539                         if (!*s)
1540                         {
1541                                 Mem_Free(lightsstring);
1542                                 Con_Printf("lights file must end with a newline\n");
1543                                 return;
1544                         }
1545                         s++;
1546                         numlights++;
1547                 }
1548                 loadmodel->brushq1.lights = (mlight_t *)Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
1549                 s = lightsstring;
1550                 n = 0;
1551                 while (*s && n < numlights)
1552                 {
1553                         t = s;
1554                         while (*s && *s != '\n' && *s != '\r')
1555                                 s++;
1556                         if (!*s)
1557                         {
1558                                 Con_Printf("misparsed lights file!\n");
1559                                 break;
1560                         }
1561                         e = loadmodel->brushq1.lights + n;
1562                         tempchar = *s;
1563                         *s = 0;
1564                         a = sscanf(t, "%f %f %f %f %f %f %f %f %f %f %f %f %f %d", &e->origin[0], &e->origin[1], &e->origin[2], &e->falloff, &e->light[0], &e->light[1], &e->light[2], &e->subtract, &e->spotdir[0], &e->spotdir[1], &e->spotdir[2], &e->spotcone, &e->distbias, &e->style);
1565                         *s = tempchar;
1566                         if (a != 14)
1567                         {
1568                                 Con_Printf("invalid lights file, found %d parameters on line %i, should be 14 parameters (origin[0] origin[1] origin[2] falloff light[0] light[1] light[2] subtract spotdir[0] spotdir[1] spotdir[2] spotcone distancebias style)\n", a, n + 1);
1569                                 break;
1570                         }
1571                         if (*s == '\r')
1572                                 s++;
1573                         if (*s == '\n')
1574                                 s++;
1575                         n++;
1576                 }
1577                 if (*s)
1578                         Con_Printf("misparsed lights file!\n");
1579                 loadmodel->brushq1.numlights = numlights;
1580                 Mem_Free(lightsstring);
1581         }
1582 }
1583
1584 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1585 {
1586         loadmodel->brushq1.num_compressedpvs = 0;
1587         loadmodel->brushq1.data_compressedpvs = NULL;
1588         if (!l->filelen)
1589                 return;
1590         loadmodel->brushq1.num_compressedpvs = l->filelen;
1591         loadmodel->brushq1.data_compressedpvs = (unsigned char *)Mem_Alloc(loadmodel->mempool, l->filelen);
1592         memcpy(loadmodel->brushq1.data_compressedpvs, mod_base + l->fileofs, l->filelen);
1593 }
1594
1595 // used only for HalfLife maps
1596 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1597 {
1598         char key[128], value[4096];
1599         char wadname[128];
1600         int i, j, k;
1601         if (!data)
1602                 return;
1603         if (!COM_ParseToken(&data, false))
1604                 return; // error
1605         if (com_token[0] != '{')
1606                 return; // error
1607         while (1)
1608         {
1609                 if (!COM_ParseToken(&data, false))
1610                         return; // error
1611                 if (com_token[0] == '}')
1612                         break; // end of worldspawn
1613                 if (com_token[0] == '_')
1614                         strcpy(key, com_token + 1);
1615                 else
1616                         strcpy(key, com_token);
1617                 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1618                         key[strlen(key)-1] = 0;
1619                 if (!COM_ParseToken(&data, false))
1620                         return; // error
1621                 dpsnprintf(value, sizeof(value), "%s", com_token);
1622                 if (!strcmp("wad", key)) // for HalfLife maps
1623                 {
1624                         if (loadmodel->brush.ishlbsp)
1625                         {
1626                                 j = 0;
1627                                 for (i = 0;i < (int)sizeof(value);i++)
1628                                         if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1629                                                 break;
1630                                 if (value[i])
1631                                 {
1632                                         for (;i < (int)sizeof(value);i++)
1633                                         {
1634                                                 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1635                                                 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1636                                                         j = i+1;
1637                                                 else if (value[i] == ';' || value[i] == 0)
1638                                                 {
1639                                                         k = value[i];
1640                                                         value[i] = 0;
1641                                                         strcpy(wadname, "textures/");
1642                                                         strcat(wadname, &value[j]);
1643                                                         W_LoadTextureWadFile(wadname, false);
1644                                                         j = i+1;
1645                                                         if (!k)
1646                                                                 break;
1647                                                 }
1648                                         }
1649                                 }
1650                         }
1651                 }
1652         }
1653 }
1654
1655 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1656 {
1657         loadmodel->brush.entities = NULL;
1658         if (!l->filelen)
1659                 return;
1660         loadmodel->brush.entities = (char *)Mem_Alloc(loadmodel->mempool, l->filelen);
1661         memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1662         if (loadmodel->brush.ishlbsp)
1663                 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1664 }
1665
1666
1667 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1668 {
1669         dvertex_t       *in;
1670         mvertex_t       *out;
1671         int                     i, count;
1672
1673         in = (dvertex_t *)(mod_base + l->fileofs);
1674         if (l->filelen % sizeof(*in))
1675                 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1676         count = l->filelen / sizeof(*in);
1677         out = (mvertex_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1678
1679         loadmodel->brushq1.vertexes = out;
1680         loadmodel->brushq1.numvertexes = count;
1681
1682         for ( i=0 ; i<count ; i++, in++, out++)
1683         {
1684                 out->position[0] = LittleFloat(in->point[0]);
1685                 out->position[1] = LittleFloat(in->point[1]);
1686                 out->position[2] = LittleFloat(in->point[2]);
1687         }
1688 }
1689
1690 // The following two functions should be removed and MSG_* or SZ_* function sets adjusted so they
1691 // can be used for this
1692 // REMOVEME
1693 int SB_ReadInt (unsigned char **buffer)
1694 {
1695         int     i;
1696         i = ((*buffer)[0]) + 256*((*buffer)[1]) + 65536*((*buffer)[2]) + 16777216*((*buffer)[3]);
1697         (*buffer) += 4;
1698         return i;
1699 }
1700
1701 // REMOVEME
1702 float SB_ReadFloat (unsigned char **buffer)
1703 {
1704         union
1705         {
1706                 int             i;
1707                 float   f;
1708         } u;
1709
1710         u.i = SB_ReadInt (buffer);
1711         return u.f;
1712 }
1713
1714 static void Mod_Q1BSP_LoadSubmodels(lump_t *l, hullinfo_t *hullinfo)
1715 {
1716         unsigned char           *index;
1717         dmodel_t        *out;
1718         int                     i, j, count;
1719
1720         index = (unsigned char *)(mod_base + l->fileofs);
1721         if (l->filelen % (48+4*hullinfo->filehulls))
1722                 Host_Error ("Mod_Q1BSP_LoadSubmodels: funny lump size in %s", loadmodel->name);
1723
1724         count = l->filelen / (48+4*hullinfo->filehulls);
1725         out = (dmodel_t *)Mem_Alloc (loadmodel->mempool, count*sizeof(*out));
1726
1727         loadmodel->brushq1.submodels = out;
1728         loadmodel->brush.numsubmodels = count;
1729
1730         for (i = 0; i < count; i++, out++)
1731         {
1732         // spread out the mins / maxs by a pixel
1733                 out->mins[0] = SB_ReadFloat (&index) - 1;
1734                 out->mins[1] = SB_ReadFloat (&index) - 1;
1735                 out->mins[2] = SB_ReadFloat (&index) - 1;
1736                 out->maxs[0] = SB_ReadFloat (&index) + 1;
1737                 out->maxs[1] = SB_ReadFloat (&index) + 1;
1738                 out->maxs[2] = SB_ReadFloat (&index) + 1;
1739                 out->origin[0] = SB_ReadFloat (&index);
1740                 out->origin[1] = SB_ReadFloat (&index);
1741                 out->origin[2] = SB_ReadFloat (&index);
1742                 for (j = 0; j < hullinfo->filehulls; j++)
1743                         out->headnode[j] = SB_ReadInt (&index);
1744                 out->visleafs = SB_ReadInt (&index);
1745                 out->firstface = SB_ReadInt (&index);
1746                 out->numfaces = SB_ReadInt (&index);
1747         }
1748 }
1749
1750 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1751 {
1752         dedge_t *in;
1753         medge_t *out;
1754         int     i, count;
1755
1756         in = (dedge_t *)(mod_base + l->fileofs);
1757         if (l->filelen % sizeof(*in))
1758                 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1759         count = l->filelen / sizeof(*in);
1760         out = (medge_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1761
1762         loadmodel->brushq1.edges = out;
1763         loadmodel->brushq1.numedges = count;
1764
1765         for ( i=0 ; i<count ; i++, in++, out++)
1766         {
1767                 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1768                 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1769                 if (out->v[0] >= loadmodel->brushq1.numvertexes || out->v[1] >= loadmodel->brushq1.numvertexes)
1770                 {
1771                         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);
1772                         out->v[0] = 0;
1773                         out->v[1] = 0;
1774                 }
1775         }
1776 }
1777
1778 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1779 {
1780         texinfo_t *in;
1781         mtexinfo_t *out;
1782         int i, j, k, count, miptex;
1783
1784         in = (texinfo_t *)(mod_base + l->fileofs);
1785         if (l->filelen % sizeof(*in))
1786                 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1787         count = l->filelen / sizeof(*in);
1788         out = (mtexinfo_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1789
1790         loadmodel->brushq1.texinfo = out;
1791         loadmodel->brushq1.numtexinfo = count;
1792
1793         for (i = 0;i < count;i++, in++, out++)
1794         {
1795                 for (k = 0;k < 2;k++)
1796                         for (j = 0;j < 4;j++)
1797                                 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1798
1799                 miptex = LittleLong(in->miptex);
1800                 out->flags = LittleLong(in->flags);
1801
1802                 out->texture = NULL;
1803                 if (loadmodel->data_textures)
1804                 {
1805                         if ((unsigned int) miptex >= (unsigned int) loadmodel->num_textures)
1806                                 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->num_textures);
1807                         else
1808                                 out->texture = loadmodel->data_textures + miptex;
1809                 }
1810                 if (out->flags & TEX_SPECIAL)
1811                 {
1812                         // if texture chosen is NULL or the shader needs a lightmap,
1813                         // force to notexture water shader
1814                         if (out->texture == NULL || out->texture->basematerialflags & MATERIALFLAG_WALL)
1815                                 out->texture = loadmodel->data_textures + (loadmodel->num_textures - 1);
1816                 }
1817                 else
1818                 {
1819                         // if texture chosen is NULL, force to notexture
1820                         if (out->texture == NULL)
1821                                 out->texture = loadmodel->data_textures + (loadmodel->num_textures - 2);
1822                 }
1823         }
1824 }
1825
1826 #if 0
1827 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1828 {
1829         int             i, j;
1830         float   *v;
1831
1832         mins[0] = mins[1] = mins[2] = 9999;
1833         maxs[0] = maxs[1] = maxs[2] = -9999;
1834         v = verts;
1835         for (i = 0;i < numverts;i++)
1836         {
1837                 for (j = 0;j < 3;j++, v++)
1838                 {
1839                         if (*v < mins[j])
1840                                 mins[j] = *v;
1841                         if (*v > maxs[j])
1842                                 maxs[j] = *v;
1843                 }
1844         }
1845 }
1846
1847 #define MAX_SUBDIVPOLYTRIANGLES 4096
1848 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1849
1850 static int subdivpolyverts, subdivpolytriangles;
1851 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1852 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1853
1854 static int subdivpolylookupvert(vec3_t v)
1855 {
1856         int i;
1857         for (i = 0;i < subdivpolyverts;i++)
1858                 if (subdivpolyvert[i][0] == v[0]
1859                  && subdivpolyvert[i][1] == v[1]
1860                  && subdivpolyvert[i][2] == v[2])
1861                         return i;
1862         if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1863                 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1864         VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1865         return subdivpolyverts++;
1866 }
1867
1868 static void SubdividePolygon(int numverts, float *verts)
1869 {
1870         int             i, i1, i2, i3, f, b, c, p;
1871         vec3_t  mins, maxs, front[256], back[256];
1872         float   m, *pv, *cv, dist[256], frac;
1873
1874         if (numverts > 250)
1875                 Host_Error("SubdividePolygon: ran out of verts in buffer");
1876
1877         BoundPoly(numverts, verts, mins, maxs);
1878
1879         for (i = 0;i < 3;i++)
1880         {
1881                 m = (mins[i] + maxs[i]) * 0.5;
1882                 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1883                 if (maxs[i] - m < 8)
1884                         continue;
1885                 if (m - mins[i] < 8)
1886                         continue;
1887
1888                 // cut it
1889                 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1890                         dist[c] = cv[i] - m;
1891
1892                 f = b = 0;
1893                 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1894                 {
1895                         if (dist[p] >= 0)
1896                         {
1897                                 VectorCopy(pv, front[f]);
1898                                 f++;
1899                         }
1900                         if (dist[p] <= 0)
1901                         {
1902                                 VectorCopy(pv, back[b]);
1903                                 b++;
1904                         }
1905                         if (dist[p] == 0 || dist[c] == 0)
1906                                 continue;
1907                         if ((dist[p] > 0) != (dist[c] > 0) )
1908                         {
1909                                 // clip point
1910                                 frac = dist[p] / (dist[p] - dist[c]);
1911                                 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1912                                 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1913                                 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1914                                 f++;
1915                                 b++;
1916                         }
1917                 }
1918
1919                 SubdividePolygon(f, front[0]);
1920                 SubdividePolygon(b, back[0]);
1921                 return;
1922         }
1923
1924         i1 = subdivpolylookupvert(verts);
1925         i2 = subdivpolylookupvert(verts + 3);
1926         for (i = 2;i < numverts;i++)
1927         {
1928                 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1929                 {
1930                         Con_Print("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1931                         return;
1932                 }
1933
1934                 i3 = subdivpolylookupvert(verts + i * 3);
1935                 subdivpolyindex[subdivpolytriangles][0] = i1;
1936                 subdivpolyindex[subdivpolytriangles][1] = i2;
1937                 subdivpolyindex[subdivpolytriangles][2] = i3;
1938                 i2 = i3;
1939                 subdivpolytriangles++;
1940         }
1941 }
1942
1943 //Breaks a polygon up along axial 64 unit
1944 //boundaries so that turbulent and sky warps
1945 //can be done reasonably.
1946 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surface)
1947 {
1948         int i, j;
1949         surfvertex_t *v;
1950         surfmesh_t *mesh;
1951
1952         subdivpolytriangles = 0;
1953         subdivpolyverts = 0;
1954         SubdividePolygon(surface->num_vertices, (surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex));
1955         if (subdivpolytriangles < 1)
1956                 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?");
1957
1958         surface->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1959         mesh->num_vertices = subdivpolyverts;
1960         mesh->num_triangles = subdivpolytriangles;
1961         mesh->vertex = (surfvertex_t *)(mesh + 1);
1962         mesh->index = (int *)(mesh->vertex + mesh->num_vertices);
1963         memset(mesh->vertex, 0, mesh->num_vertices * sizeof(surfvertex_t));
1964
1965         for (i = 0;i < mesh->num_triangles;i++)
1966                 for (j = 0;j < 3;j++)
1967                         mesh->index[i*3+j] = subdivpolyindex[i][j];
1968
1969         for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1970         {
1971                 VectorCopy(subdivpolyvert[i], v->v);
1972                 v->st[0] = DotProduct(v->v, surface->lightmapinfo->texinfo->vecs[0]);
1973                 v->st[1] = DotProduct(v->v, surface->lightmapinfo->texinfo->vecs[1]);
1974         }
1975 }
1976 #endif
1977
1978 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1979 {
1980         dface_t *in;
1981         msurface_t *surface;
1982         int i, j, count, surfacenum, planenum, smax, tmax, ssize, tsize, firstedge, numedges, totalverts, totaltris;
1983         float texmins[2], texmaxs[2], val;
1984
1985         in = (dface_t *)(mod_base + l->fileofs);
1986         if (l->filelen % sizeof(*in))
1987                 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1988         count = l->filelen / sizeof(*in);
1989         loadmodel->data_surfaces = (msurface_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1990         loadmodel->data_surfaces_lightmapinfo = (msurface_lightmapinfo_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_lightmapinfo_t));
1991
1992         loadmodel->num_surfaces = count;
1993
1994         totalverts = 0;
1995         totaltris = 0;
1996         for (surfacenum = 0, in = (dface_t *)(mod_base + l->fileofs);surfacenum < count;surfacenum++, in++)
1997         {
1998                 numedges = LittleShort(in->numedges);
1999                 totalverts += numedges;
2000                 totaltris += numedges - 2;
2001         }
2002
2003         // TODO: split up into multiple meshes as needed to avoid exceeding 65536
2004         // vertex limit
2005         loadmodel->nummeshes = 1;
2006         loadmodel->meshlist = (surfmesh_t **)Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t *));
2007         loadmodel->meshlist[0] = Mod_AllocSurfMesh(loadmodel->mempool, totalverts, totaltris, true, false, false);
2008
2009         totalverts = 0;
2010         totaltris = 0;
2011         for (surfacenum = 0, in = (dface_t *)(mod_base + l->fileofs), surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, in++, surface++)
2012         {
2013                 surface->lightmapinfo = loadmodel->data_surfaces_lightmapinfo + surfacenum;
2014
2015                 // FIXME: validate edges, texinfo, etc?
2016                 firstedge = LittleLong(in->firstedge);
2017                 numedges = LittleShort(in->numedges);
2018                 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)
2019                         Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)", firstedge, numedges, loadmodel->brushq1.numsurfedges);
2020                 i = LittleShort(in->texinfo);
2021                 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
2022                         Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)", i, loadmodel->brushq1.numtexinfo);
2023                 surface->lightmapinfo->texinfo = loadmodel->brushq1.texinfo + i;
2024                 surface->texture = surface->lightmapinfo->texinfo->texture;
2025
2026                 planenum = LittleShort(in->planenum);
2027                 if ((unsigned int) planenum >= (unsigned int) loadmodel->brush.num_planes)
2028                         Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)", planenum, loadmodel->brush.num_planes);
2029
2030                 //surface->flags = surface->texture->flags;
2031                 //if (LittleShort(in->side))
2032                 //      surface->flags |= SURF_PLANEBACK;
2033                 //surface->plane = loadmodel->brush.data_planes + planenum;
2034
2035                 surface->groupmesh = loadmodel->meshlist[0];
2036                 surface->num_firstvertex = totalverts;
2037                 surface->num_vertices = numedges;
2038                 surface->num_firsttriangle = totaltris;
2039                 surface->num_triangles = numedges - 2;
2040                 totalverts += numedges;
2041                 totaltris += numedges - 2;
2042
2043                 // convert edges back to a normal polygon
2044                 for (i = 0;i < surface->num_vertices;i++)
2045                 {
2046                         int lindex = loadmodel->brushq1.surfedges[firstedge + i];
2047                         float s, t;
2048                         if (lindex > 0)
2049                                 VectorCopy(loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position, (surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex) + i * 3);
2050                         else
2051                                 VectorCopy(loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position, (surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex) + i * 3);
2052                         s = DotProduct(((surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex) + i * 3), surface->lightmapinfo->texinfo->vecs[0]) + surface->lightmapinfo->texinfo->vecs[0][3];
2053                         t = DotProduct(((surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex) + i * 3), surface->lightmapinfo->texinfo->vecs[1]) + surface->lightmapinfo->texinfo->vecs[1][3];
2054                         (surface->groupmesh->data_texcoordtexture2f + 2 * surface->num_firstvertex)[i * 2 + 0] = s / surface->texture->width;
2055                         (surface->groupmesh->data_texcoordtexture2f + 2 * surface->num_firstvertex)[i * 2 + 1] = t / surface->texture->height;
2056                         (surface->groupmesh->data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 0] = 0;
2057                         (surface->groupmesh->data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 1] = 0;
2058                         (surface->groupmesh->data_lightmapoffsets + surface->num_firstvertex)[i] = 0;
2059                 }
2060
2061                 for (i = 0;i < surface->num_triangles;i++)
2062                 {
2063                         (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle)[i * 3 + 0] = 0 + surface->num_firstvertex;
2064                         (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle)[i * 3 + 1] = i + 1 + surface->num_firstvertex;
2065                         (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle)[i * 3 + 2] = i + 2 + surface->num_firstvertex;
2066                 }
2067
2068                 // compile additional data about the surface geometry
2069                 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);
2070                 BoxFromPoints(surface->mins, surface->maxs, surface->num_vertices, (surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex));
2071
2072                 // generate surface extents information
2073                 texmins[0] = texmaxs[0] = DotProduct((surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex), surface->lightmapinfo->texinfo->vecs[0]) + surface->lightmapinfo->texinfo->vecs[0][3];
2074                 texmins[1] = texmaxs[1] = DotProduct((surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex), surface->lightmapinfo->texinfo->vecs[1]) + surface->lightmapinfo->texinfo->vecs[1][3];
2075                 for (i = 1;i < surface->num_vertices;i++)
2076                 {
2077                         for (j = 0;j < 2;j++)
2078                         {
2079                                 val = DotProduct((surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex) + i * 3, surface->lightmapinfo->texinfo->vecs[j]) + surface->lightmapinfo->texinfo->vecs[j][3];
2080                                 texmins[j] = min(texmins[j], val);
2081                                 texmaxs[j] = max(texmaxs[j], val);
2082                         }
2083                 }
2084                 for (i = 0;i < 2;i++)
2085                 {
2086                         surface->lightmapinfo->texturemins[i] = (int) floor(texmins[i] / 16.0) * 16;
2087                         surface->lightmapinfo->extents[i] = (int) ceil(texmaxs[i] / 16.0) * 16 - surface->lightmapinfo->texturemins[i];
2088                 }
2089
2090                 smax = surface->lightmapinfo->extents[0] >> 4;
2091                 tmax = surface->lightmapinfo->extents[1] >> 4;
2092                 ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
2093                 tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
2094
2095                 // lighting info
2096                 for (i = 0;i < MAXLIGHTMAPS;i++)
2097                         surface->lightmapinfo->styles[i] = in->styles[i];
2098                 surface->lightmapinfo->lightmaptexturestride = 0;
2099                 surface->lightmaptexture = NULL;
2100                 i = LittleLong(in->lightofs);
2101                 if (i == -1)
2102                 {
2103                         surface->lightmapinfo->samples = NULL;
2104                         // give non-lightmapped water a 1x white lightmap
2105                         if ((surface->texture->basematerialflags & MATERIALFLAG_WATER) && (surface->lightmapinfo->texinfo->flags & TEX_SPECIAL) && ssize <= 256 && tsize <= 256)
2106                         {
2107                                 surface->lightmapinfo->samples = (unsigned char *)Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
2108                                 surface->lightmapinfo->styles[0] = 0;
2109                                 memset(surface->lightmapinfo->samples, 128, ssize * tsize * 3);
2110                         }
2111                 }
2112                 else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
2113                         surface->lightmapinfo->samples = loadmodel->brushq1.lightdata + i;
2114                 else // LordHavoc: white lighting (bsp version 29)
2115                         surface->lightmapinfo->samples = loadmodel->brushq1.lightdata + (i * 3);
2116
2117                 // check if we should apply a lightmap to this
2118                 if (!(surface->lightmapinfo->texinfo->flags & TEX_SPECIAL) || surface->lightmapinfo->samples)
2119                 {
2120                         int i, iu, iv;
2121                         float u, v, ubase, vbase, uscale, vscale;
2122
2123                         if (ssize > 256 || tsize > 256)
2124                                 Host_Error("Bad surface extents");
2125                         // force lightmap upload on first time seeing the surface
2126                         surface->cached_dlight = true;
2127                         // stainmap for permanent marks on walls
2128                         surface->lightmapinfo->stainsamples = (unsigned char *)Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
2129                         // clear to white
2130                         memset(surface->lightmapinfo->stainsamples, 255, ssize * tsize * 3);
2131
2132                         if (r_miplightmaps.integer)
2133                         {
2134                                 surface->lightmapinfo->lightmaptexturestride = ssize;
2135                                 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);
2136                         }
2137                         else
2138                         {
2139                                 surface->lightmapinfo->lightmaptexturestride = R_CompatibleFragmentWidth(ssize, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
2140                                 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);
2141                         }
2142                         R_FragmentLocation(surface->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
2143                         uscale = (uscale - ubase) / ssize;
2144                         vscale = (vscale - vbase) / tsize;
2145
2146                         for (i = 0;i < surface->num_vertices;i++)
2147                         {
2148                                 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);
2149                                 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);
2150                                 (surface->groupmesh->data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 0] = u * uscale + ubase;
2151                                 (surface->groupmesh->data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 1] = v * vscale + vbase;
2152                                 // LordHavoc: calc lightmap data offset for vertex lighting to use
2153                                 iu = (int) u;
2154                                 iv = (int) v;
2155                                 (surface->groupmesh->data_lightmapoffsets + surface->num_firstvertex)[i] = (bound(0, iv, tmax) * ssize + bound(0, iu, smax)) * 3;
2156                         }
2157                 }
2158         }
2159 }
2160
2161 static void Mod_Q1BSP_LoadNodes_RecursiveSetParent(mnode_t *node, mnode_t *parent)
2162 {
2163         //if (node->parent)
2164         //      Host_Error("Mod_Q1BSP_LoadNodes_RecursiveSetParent: runaway recursion");
2165         node->parent = parent;
2166         if (node->plane)
2167         {
2168                 Mod_Q1BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
2169                 Mod_Q1BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
2170         }
2171 }
2172
2173 static void Mod_Q1BSP_LoadNodes(lump_t *l)
2174 {
2175         int                     i, j, count, p;
2176         dnode_t         *in;
2177         mnode_t         *out;
2178
2179         in = (dnode_t *)(mod_base + l->fileofs);
2180         if (l->filelen % sizeof(*in))
2181                 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
2182         count = l->filelen / sizeof(*in);
2183         out = (mnode_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2184
2185         loadmodel->brush.data_nodes = out;
2186         loadmodel->brush.num_nodes = count;
2187
2188         for ( i=0 ; i<count ; i++, in++, out++)
2189         {
2190                 for (j=0 ; j<3 ; j++)
2191                 {
2192                         out->mins[j] = LittleShort(in->mins[j]);
2193                         out->maxs[j] = LittleShort(in->maxs[j]);
2194                 }
2195
2196                 p = LittleLong(in->planenum);
2197                 out->plane = loadmodel->brush.data_planes + p;
2198
2199                 out->firstsurface = LittleShort(in->firstface);
2200                 out->numsurfaces = LittleShort(in->numfaces);
2201
2202                 for (j=0 ; j<2 ; j++)
2203                 {
2204                         p = LittleShort(in->children[j]);
2205                         if (p >= 0)
2206                                 out->children[j] = loadmodel->brush.data_nodes + p;
2207                         else
2208                                 out->children[j] = (mnode_t *)(loadmodel->brush.data_leafs + (-1 - p));
2209                 }
2210         }
2211
2212         Mod_Q1BSP_LoadNodes_RecursiveSetParent(loadmodel->brush.data_nodes, NULL);      // sets nodes and leafs
2213 }
2214
2215 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
2216 {
2217         dleaf_t *in;
2218         mleaf_t *out;
2219         int i, j, count, p;
2220
2221         in = (dleaf_t *)(mod_base + l->fileofs);
2222         if (l->filelen % sizeof(*in))
2223                 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
2224         count = l->filelen / sizeof(*in);
2225         out = (mleaf_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2226
2227         loadmodel->brush.data_leafs = out;
2228         loadmodel->brush.num_leafs = count;
2229         // get visleafs from the submodel data
2230         loadmodel->brush.num_pvsclusters = loadmodel->brushq1.submodels[0].visleafs;
2231         loadmodel->brush.num_pvsclusterbytes = (loadmodel->brush.num_pvsclusters+7)>>3;
2232         loadmodel->brush.data_pvsclusters = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_pvsclusters * loadmodel->brush.num_pvsclusterbytes);
2233         memset(loadmodel->brush.data_pvsclusters, 0xFF, loadmodel->brush.num_pvsclusters * loadmodel->brush.num_pvsclusterbytes);
2234
2235         for ( i=0 ; i<count ; i++, in++, out++)
2236         {
2237                 for (j=0 ; j<3 ; j++)
2238                 {
2239                         out->mins[j] = LittleShort(in->mins[j]);
2240                         out->maxs[j] = LittleShort(in->maxs[j]);
2241                 }
2242
2243                 // FIXME: this function could really benefit from some error checking
2244
2245                 out->contents = LittleLong(in->contents);
2246
2247                 out->firstleafsurface = loadmodel->brush.data_leafsurfaces + LittleShort(in->firstmarksurface);
2248                 out->numleafsurfaces = LittleShort(in->nummarksurfaces);
2249                 if (out->firstleafsurface < 0 || LittleShort(in->firstmarksurface) + out->numleafsurfaces > loadmodel->brush.num_leafsurfaces)
2250                 {
2251                         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);
2252                         out->firstleafsurface = NULL;
2253                         out->numleafsurfaces = 0;
2254                 }
2255
2256                 out->clusterindex = i - 1;
2257                 if (out->clusterindex >= loadmodel->brush.num_pvsclusters)
2258                         out->clusterindex = -1;
2259
2260                 p = LittleLong(in->visofs);
2261                 // ignore visofs errors on leaf 0 (solid)
2262                 if (p >= 0 && out->clusterindex >= 0)
2263                 {
2264                         if (p >= loadmodel->brushq1.num_compressedpvs)
2265                                 Con_Print("Mod_Q1BSP_LoadLeafs: invalid visofs\n");
2266                         else
2267                                 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);
2268                 }
2269
2270                 for (j = 0;j < 4;j++)
2271                         out->ambient_sound_level[j] = in->ambient_level[j];
2272
2273                 // FIXME: Insert caustics here
2274         }
2275 }
2276
2277 static void Mod_Q1BSP_LoadClipnodes(lump_t *l, hullinfo_t *hullinfo)
2278 {
2279         dclipnode_t *in, *out;
2280         int                     i, count;
2281         hull_t          *hull;
2282
2283         in = (dclipnode_t *)(mod_base + l->fileofs);
2284         if (l->filelen % sizeof(*in))
2285                 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
2286         count = l->filelen / sizeof(*in);
2287         out = (dclipnode_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2288
2289         loadmodel->brushq1.clipnodes = out;
2290         loadmodel->brushq1.numclipnodes = count;
2291
2292         for (i = 1; i < hullinfo->numhulls; i++)
2293         {
2294                 hull = &loadmodel->brushq1.hulls[i];
2295                 hull->clipnodes = out;
2296                 hull->firstclipnode = 0;
2297                 hull->lastclipnode = count-1;
2298                 hull->planes = loadmodel->brush.data_planes;
2299                 hull->clip_mins[0] = hullinfo->hullsizes[i][0][0];
2300                 hull->clip_mins[1] = hullinfo->hullsizes[i][0][1];
2301                 hull->clip_mins[2] = hullinfo->hullsizes[i][0][2];
2302                 hull->clip_maxs[0] = hullinfo->hullsizes[i][1][0];
2303                 hull->clip_maxs[1] = hullinfo->hullsizes[i][1][1];
2304                 hull->clip_maxs[2] = hullinfo->hullsizes[i][1][2];
2305                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2306         }
2307
2308         for (i=0 ; i<count ; i++, out++, in++)
2309         {
2310                 out->planenum = LittleLong(in->planenum);
2311                 out->children[0] = LittleShort(in->children[0]);
2312                 out->children[1] = LittleShort(in->children[1]);
2313                 if (out->children[0] >= count || out->children[1] >= count)
2314                         Host_Error("Corrupt clipping hull(out of range child)");
2315         }
2316 }
2317
2318 //Duplicate the drawing hull structure as a clipping hull
2319 static void Mod_Q1BSP_MakeHull0(void)
2320 {
2321         mnode_t         *in;
2322         dclipnode_t *out;
2323         int                     i;
2324         hull_t          *hull;
2325
2326         hull = &loadmodel->brushq1.hulls[0];
2327
2328         in = loadmodel->brush.data_nodes;
2329         out = (dclipnode_t *)Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_nodes * sizeof(dclipnode_t));
2330
2331         hull->clipnodes = out;
2332         hull->firstclipnode = 0;
2333         hull->lastclipnode = loadmodel->brush.num_nodes - 1;
2334         hull->planes = loadmodel->brush.data_planes;
2335
2336         for (i = 0;i < loadmodel->brush.num_nodes;i++, out++, in++)
2337         {
2338                 out->planenum = in->plane - loadmodel->brush.data_planes;
2339                 out->children[0] = in->children[0]->plane ? in->children[0] - loadmodel->brush.data_nodes : ((mleaf_t *)in->children[0])->contents;
2340                 out->children[1] = in->children[1]->plane ? in->children[1] - loadmodel->brush.data_nodes : ((mleaf_t *)in->children[1])->contents;
2341         }
2342 }
2343
2344 static void Mod_Q1BSP_LoadLeaffaces(lump_t *l)
2345 {
2346         int i, j;
2347         short *in;
2348
2349         in = (short *)(mod_base + l->fileofs);
2350         if (l->filelen % sizeof(*in))
2351                 Host_Error("Mod_Q1BSP_LoadLeaffaces: funny lump size in %s",loadmodel->name);
2352         loadmodel->brush.num_leafsurfaces = l->filelen / sizeof(*in);
2353         loadmodel->brush.data_leafsurfaces = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_leafsurfaces * sizeof(int));
2354
2355         for (i = 0;i < loadmodel->brush.num_leafsurfaces;i++)
2356         {
2357                 j = (unsigned) LittleShort(in[i]);
2358                 if (j >= loadmodel->num_surfaces)
2359                         Host_Error("Mod_Q1BSP_LoadLeaffaces: bad surface number");
2360                 loadmodel->brush.data_leafsurfaces[i] = j;
2361         }
2362 }
2363
2364 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
2365 {
2366         int             i;
2367         int             *in;
2368
2369         in = (int *)(mod_base + l->fileofs);
2370         if (l->filelen % sizeof(*in))
2371                 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
2372         loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
2373         loadmodel->brushq1.surfedges = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
2374
2375         for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
2376                 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
2377 }
2378
2379
2380 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
2381 {
2382         int                     i;
2383         mplane_t        *out;
2384         dplane_t        *in;
2385
2386         in = (dplane_t *)(mod_base + l->fileofs);
2387         if (l->filelen % sizeof(*in))
2388                 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
2389
2390         loadmodel->brush.num_planes = l->filelen / sizeof(*in);
2391         loadmodel->brush.data_planes = out = (mplane_t *)Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_planes * sizeof(*out));
2392
2393         for (i = 0;i < loadmodel->brush.num_planes;i++, in++, out++)
2394         {
2395                 out->normal[0] = LittleFloat(in->normal[0]);
2396                 out->normal[1] = LittleFloat(in->normal[1]);
2397                 out->normal[2] = LittleFloat(in->normal[2]);
2398                 out->dist = LittleFloat(in->dist);
2399
2400                 PlaneClassify(out);
2401         }
2402 }
2403
2404 static void Mod_Q1BSP_LoadMapBrushes(void)
2405 {
2406 #if 0
2407 // unfinished
2408         int submodel, numbrushes;
2409         qboolean firstbrush;
2410         char *text, *maptext;
2411         char mapfilename[MAX_QPATH];
2412         FS_StripExtension (loadmodel->name, mapfilename, sizeof (mapfilename));
2413         strlcat (mapfilename, ".map", sizeof (mapfilename));
2414         maptext = (unsigned char*) FS_LoadFile(mapfilename, tempmempool, false, NULL);
2415         if (!maptext)
2416                 return;
2417         text = maptext;
2418         if (!COM_ParseToken(&data, false))
2419                 return; // error
2420         submodel = 0;
2421         for (;;)
2422         {
2423                 if (!COM_ParseToken(&data, false))
2424                         break;
2425                 if (com_token[0] != '{')
2426                         return; // error
2427                 // entity
2428                 firstbrush = true;
2429                 numbrushes = 0;
2430                 maxbrushes = 256;
2431                 brushes = Mem_Alloc(loadmodel->mempool, maxbrushes * sizeof(mbrush_t));
2432                 for (;;)
2433                 {
2434                         if (!COM_ParseToken(&data, false))
2435                                 return; // error
2436                         if (com_token[0] == '}')
2437                                 break; // end of entity
2438                         if (com_token[0] == '{')
2439                         {
2440                                 // brush
2441                                 if (firstbrush)
2442                                 {
2443                                         if (submodel)
2444                                         {
2445                                                 if (submodel > loadmodel->brush.numsubmodels)
2446                                                 {
2447                                                         Con_Printf("Mod_Q1BSP_LoadMapBrushes: .map has more submodels than .bsp!\n");
2448                                                         model = NULL;
2449                                                 }
2450                                                 else
2451                                                         model = loadmodel->brush.submodels[submodel];
2452                                         }
2453                                         else
2454                                                 model = loadmodel;
2455                                 }
2456                                 for (;;)
2457                                 {
2458                                         if (!COM_ParseToken(&data, false))
2459                                                 return; // error
2460                                         if (com_token[0] == '}')
2461                                                 break; // end of brush
2462                                         // each brush face should be this format:
2463                                         // ( x y z ) ( x y z ) ( x y z ) texture scroll_s scroll_t rotateangle scale_s scale_t
2464                                         // FIXME: support hl .map format
2465                                         for (pointnum = 0;pointnum < 3;pointnum++)
2466                                         {
2467                                                 COM_ParseToken(&data, false);
2468                                                 for (componentnum = 0;componentnum < 3;componentnum++)
2469                                                 {
2470                                                         COM_ParseToken(&data, false);
2471                                                         point[pointnum][componentnum] = atof(com_token);
2472                                                 }
2473                                                 COM_ParseToken(&data, false);
2474                                         }
2475                                         COM_ParseToken(&data, false);
2476                                         strlcpy(facetexture, com_token, sizeof(facetexture));
2477                                         COM_ParseToken(&data, false);
2478                                         //scroll_s = atof(com_token);
2479                                         COM_ParseToken(&data, false);
2480                                         //scroll_t = atof(com_token);
2481                                         COM_ParseToken(&data, false);
2482                                         //rotate = atof(com_token);
2483                                         COM_ParseToken(&data, false);
2484                                         //scale_s = atof(com_token);
2485                                         COM_ParseToken(&data, false);
2486                                         //scale_t = atof(com_token);
2487                                         TriangleNormal(point[0], point[1], point[2], planenormal);
2488                                         VectorNormalizeDouble(planenormal);
2489                                         planedist = DotProduct(point[0], planenormal);
2490                                         //ChooseTexturePlane(planenormal, texturevector[0], texturevector[1]);
2491                                 }
2492                                 continue;
2493                         }
2494                 }
2495         }
2496 #endif
2497 }
2498
2499
2500 #define MAX_PORTALPOINTS 64
2501
2502 typedef struct portal_s
2503 {
2504         mplane_t plane;
2505         mnode_t *nodes[2];              // [0] = front side of plane
2506         struct portal_s *next[2];
2507         int numpoints;
2508         double points[3*MAX_PORTALPOINTS];
2509         struct portal_s *chain; // all portals are linked into a list
2510 }
2511 portal_t;
2512
2513 static portal_t *portalchain;
2514
2515 /*
2516 ===========
2517 AllocPortal
2518 ===========
2519 */
2520 static portal_t *AllocPortal(void)
2521 {
2522         portal_t *p;
2523         p = (portal_t *)Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2524         p->chain = portalchain;
2525         portalchain = p;
2526         return p;
2527 }
2528
2529 static void FreePortal(portal_t *p)
2530 {
2531         Mem_Free(p);
2532 }
2533
2534 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2535 {
2536         // process only nodes (leafs already had their box calculated)
2537         if (!node->plane)
2538                 return;
2539
2540         // calculate children first
2541         Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2542         Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2543
2544         // make combined bounding box from children
2545         node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2546         node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2547         node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2548         node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2549         node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2550         node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2551 }
2552
2553 static void Mod_Q1BSP_FinalizePortals(void)
2554 {
2555         int i, j, numportals, numpoints;
2556         portal_t *p, *pnext;
2557         mportal_t *portal;
2558         mvertex_t *point;
2559         mleaf_t *leaf, *endleaf;
2560
2561         // tally up portal and point counts and recalculate bounding boxes for all
2562         // leafs (because qbsp is very sloppy)
2563         leaf = loadmodel->brush.data_leafs;
2564         endleaf = leaf + loadmodel->brush.num_leafs;
2565         for (;leaf < endleaf;leaf++)
2566         {
2567                 VectorSet(leaf->mins,  2000000000,  2000000000,  2000000000);
2568                 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2569         }
2570         p = portalchain;
2571         numportals = 0;
2572         numpoints = 0;
2573         while (p)
2574         {
2575                 // note: this check must match the one below or it will usually corrupt memory
2576                 // 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
2577                 if (p->numpoints >= 3 && p->nodes[0] != p->nodes[1] && ((mleaf_t *)p->nodes[0])->clusterindex >= 0 && ((mleaf_t *)p->nodes[1])->clusterindex >= 0)
2578                 {
2579                         numportals += 2;
2580                         numpoints += p->numpoints * 2;
2581                 }
2582                 p = p->chain;
2583         }
2584         loadmodel->brush.data_portals = (mportal_t *)Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2585         loadmodel->brush.num_portals = numportals;
2586         loadmodel->brush.data_portalpoints = (mvertex_t *)((unsigned char *) loadmodel->brush.data_portals + numportals * sizeof(mportal_t));
2587         loadmodel->brush.num_portalpoints = numpoints;
2588         // clear all leaf portal chains
2589         for (i = 0;i < loadmodel->brush.num_leafs;i++)
2590                 loadmodel->brush.data_leafs[i].portals = NULL;
2591         // process all portals in the global portal chain, while freeing them
2592         portal = loadmodel->brush.data_portals;
2593         point = loadmodel->brush.data_portalpoints;
2594         p = portalchain;
2595         portalchain = NULL;
2596         while (p)
2597         {
2598                 pnext = p->chain;
2599
2600                 if (p->numpoints >= 3 && p->nodes[0] != p->nodes[1])
2601                 {
2602                         // note: this check must match the one above or it will usually corrupt memory
2603                         // 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
2604                         if (((mleaf_t *)p->nodes[0])->clusterindex >= 0 && ((mleaf_t *)p->nodes[1])->clusterindex >= 0)
2605                         {
2606                                 // first make the back to front portal(forward portal)
2607                                 portal->points = point;
2608                                 portal->numpoints = p->numpoints;
2609                                 portal->plane.dist = p->plane.dist;
2610                                 VectorCopy(p->plane.normal, portal->plane.normal);
2611                                 portal->here = (mleaf_t *)p->nodes[1];
2612                                 portal->past = (mleaf_t *)p->nodes[0];
2613                                 // copy points
2614                                 for (j = 0;j < portal->numpoints;j++)
2615                                 {
2616                                         VectorCopy(p->points + j*3, point->position);
2617                                         point++;
2618                                 }
2619                                 BoxFromPoints(portal->mins, portal->maxs, portal->numpoints, portal->points->position);
2620                                 PlaneClassify(&portal->plane);
2621
2622                                 // link into leaf's portal chain
2623                                 portal->next = portal->here->portals;
2624                                 portal->here->portals = portal;
2625
2626                                 // advance to next portal
2627                                 portal++;
2628
2629                                 // then make the front to back portal(backward portal)
2630                                 portal->points = point;
2631                                 portal->numpoints = p->numpoints;
2632                                 portal->plane.dist = -p->plane.dist;
2633                                 VectorNegate(p->plane.normal, portal->plane.normal);
2634                                 portal->here = (mleaf_t *)p->nodes[0];
2635                                 portal->past = (mleaf_t *)p->nodes[1];
2636                                 // copy points
2637                                 for (j = portal->numpoints - 1;j >= 0;j--)
2638                                 {
2639                                         VectorCopy(p->points + j*3, point->position);
2640                                         point++;
2641                                 }
2642                                 BoxFromPoints(portal->mins, portal->maxs, portal->numpoints, portal->points->position);
2643                                 PlaneClassify(&portal->plane);
2644
2645                                 // link into leaf's portal chain
2646                                 portal->next = portal->here->portals;
2647                                 portal->here->portals = portal;
2648
2649                                 // advance to next portal
2650                                 portal++;
2651                         }
2652                         // add the portal's polygon points to the leaf bounding boxes
2653                         for (i = 0;i < 2;i++)
2654                         {
2655                                 leaf = (mleaf_t *)p->nodes[i];
2656                                 for (j = 0;j < p->numpoints;j++)
2657                                 {
2658                                         if (leaf->mins[0] > p->points[j*3+0]) leaf->mins[0] = p->points[j*3+0];
2659                                         if (leaf->mins[1] > p->points[j*3+1]) leaf->mins[1] = p->points[j*3+1];
2660                                         if (leaf->mins[2] > p->points[j*3+2]) leaf->mins[2] = p->points[j*3+2];
2661                                         if (leaf->maxs[0] < p->points[j*3+0]) leaf->maxs[0] = p->points[j*3+0];
2662                                         if (leaf->maxs[1] < p->points[j*3+1]) leaf->maxs[1] = p->points[j*3+1];
2663                                         if (leaf->maxs[2] < p->points[j*3+2]) leaf->maxs[2] = p->points[j*3+2];
2664                                 }
2665                         }
2666                 }
2667                 FreePortal(p);
2668                 p = pnext;
2669         }
2670         // now recalculate the node bounding boxes from the leafs
2671         Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brush.data_nodes);
2672 }
2673
2674 /*
2675 =============
2676 AddPortalToNodes
2677 =============
2678 */
2679 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2680 {
2681         if (!front)
2682                 Host_Error("AddPortalToNodes: NULL front node");
2683         if (!back)
2684                 Host_Error("AddPortalToNodes: NULL back node");
2685         if (p->nodes[0] || p->nodes[1])
2686                 Host_Error("AddPortalToNodes: already included");
2687         // 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
2688
2689         p->nodes[0] = front;
2690         p->next[0] = (portal_t *)front->portals;
2691         front->portals = (mportal_t *)p;
2692
2693         p->nodes[1] = back;
2694         p->next[1] = (portal_t *)back->portals;
2695         back->portals = (mportal_t *)p;
2696 }
2697
2698 /*
2699 =============
2700 RemovePortalFromNode
2701 =============
2702 */
2703 static void RemovePortalFromNodes(portal_t *portal)
2704 {
2705         int i;
2706         mnode_t *node;
2707         void **portalpointer;
2708         portal_t *t;
2709         for (i = 0;i < 2;i++)
2710         {
2711                 node = portal->nodes[i];
2712
2713                 portalpointer = (void **) &node->portals;
2714                 while (1)
2715                 {
2716                         t = (portal_t *)*portalpointer;
2717                         if (!t)
2718                                 Host_Error("RemovePortalFromNodes: portal not in leaf");
2719
2720                         if (t == portal)
2721                         {
2722                                 if (portal->nodes[0] == node)
2723                                 {
2724                                         *portalpointer = portal->next[0];
2725                                         portal->nodes[0] = NULL;
2726                                 }
2727                                 else if (portal->nodes[1] == node)
2728                                 {
2729                                         *portalpointer = portal->next[1];
2730                                         portal->nodes[1] = NULL;
2731                                 }
2732                                 else
2733                                         Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2734                                 break;
2735                         }
2736
2737                         if (t->nodes[0] == node)
2738                                 portalpointer = (void **) &t->next[0];
2739                         else if (t->nodes[1] == node)
2740                                 portalpointer = (void **) &t->next[1];
2741                         else
2742                                 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2743                 }
2744         }
2745 }
2746
2747 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2748 {
2749         int i, side;
2750         mnode_t *front, *back, *other_node;
2751         mplane_t clipplane, *plane;
2752         portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2753         int numfrontpoints, numbackpoints;
2754         double frontpoints[3*MAX_PORTALPOINTS], backpoints[3*MAX_PORTALPOINTS];
2755
2756         // if a leaf, we're done
2757         if (!node->plane)
2758                 return;
2759
2760         plane = node->plane;
2761
2762         front = node->children[0];
2763         back = node->children[1];
2764         if (front == back)
2765                 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2766
2767         // create the new portal by generating a polygon for the node plane,
2768         // and clipping it by all of the other portals(which came from nodes above this one)
2769         nodeportal = AllocPortal();
2770         nodeportal->plane = *plane;
2771
2772         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);
2773         nodeportal->numpoints = 4;
2774         side = 0;       // shut up compiler warning
2775         for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2776         {
2777                 clipplane = portal->plane;
2778                 if (portal->nodes[0] == portal->nodes[1])
2779                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2780                 if (portal->nodes[0] == node)
2781                         side = 0;
2782                 else if (portal->nodes[1] == node)
2783                 {
2784                         clipplane.dist = -clipplane.dist;
2785                         VectorNegate(clipplane.normal, clipplane.normal);
2786                         side = 1;
2787                 }
2788                 else
2789                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2790
2791                 for (i = 0;i < nodeportal->numpoints*3;i++)
2792                         frontpoints[i] = nodeportal->points[i];
2793                 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);
2794                 if (nodeportal->numpoints <= 0 || nodeportal->numpoints >= MAX_PORTALPOINTS)
2795                         break;
2796         }
2797
2798         if (nodeportal->numpoints < 3)
2799         {
2800                 Con_Print("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2801                 nodeportal->numpoints = 0;
2802         }
2803         else if (nodeportal->numpoints >= MAX_PORTALPOINTS)
2804         {
2805                 Con_Print("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal has too many points\n");
2806                 nodeportal->numpoints = 0;
2807         }
2808
2809         AddPortalToNodes(nodeportal, front, back);
2810
2811         // split the portals of this node along this node's plane and assign them to the children of this node
2812         // (migrating the portals downward through the tree)
2813         for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2814         {
2815                 if (portal->nodes[0] == portal->nodes[1])
2816                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2817                 if (portal->nodes[0] == node)
2818                         side = 0;
2819                 else if (portal->nodes[1] == node)
2820                         side = 1;
2821                 else
2822                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2823                 nextportal = portal->next[side];
2824                 if (!portal->numpoints)
2825                         continue;
2826
2827                 other_node = portal->nodes[!side];
2828                 RemovePortalFromNodes(portal);
2829
2830                 // cut the portal into two portals, one on each side of the node plane
2831                 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);
2832
2833                 if (!numfrontpoints)
2834                 {
2835                         if (side == 0)
2836                                 AddPortalToNodes(portal, back, other_node);
2837                         else
2838                                 AddPortalToNodes(portal, other_node, back);
2839                         continue;
2840                 }
2841                 if (!numbackpoints)
2842                 {
2843                         if (side == 0)
2844                                 AddPortalToNodes(portal, front, other_node);
2845                         else
2846                                 AddPortalToNodes(portal, other_node, front);
2847                         continue;
2848                 }
2849
2850                 // the portal is split
2851                 splitportal = AllocPortal();
2852                 temp = splitportal->chain;
2853                 *splitportal = *portal;
2854                 splitportal->chain = temp;
2855                 for (i = 0;i < numbackpoints*3;i++)
2856                         splitportal->points[i] = backpoints[i];
2857                 splitportal->numpoints = numbackpoints;
2858                 for (i = 0;i < numfrontpoints*3;i++)
2859                         portal->points[i] = frontpoints[i];
2860                 portal->numpoints = numfrontpoints;
2861
2862                 if (side == 0)
2863                 {
2864                         AddPortalToNodes(portal, front, other_node);
2865                         AddPortalToNodes(splitportal, back, other_node);
2866                 }
2867                 else
2868                 {
2869                         AddPortalToNodes(portal, other_node, front);
2870                         AddPortalToNodes(splitportal, other_node, back);
2871                 }
2872         }
2873
2874         Mod_Q1BSP_RecursiveNodePortals(front);
2875         Mod_Q1BSP_RecursiveNodePortals(back);
2876 }
2877
2878 static void Mod_Q1BSP_MakePortals(void)
2879 {
2880         portalchain = NULL;
2881         Mod_Q1BSP_RecursiveNodePortals(loadmodel->brush.data_nodes);
2882         Mod_Q1BSP_FinalizePortals();
2883 }
2884
2885 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2886 {
2887         int i, j, stylecounts[256], totalcount, remapstyles[256];
2888         msurface_t *surface;
2889         memset(stylecounts, 0, sizeof(stylecounts));
2890         for (i = 0;i < model->nummodelsurfaces;i++)
2891         {
2892                 surface = model->data_surfaces + model->firstmodelsurface + i;
2893                 for (j = 0;j < MAXLIGHTMAPS;j++)
2894                         stylecounts[surface->lightmapinfo->styles[j]]++;
2895         }
2896         totalcount = 0;
2897         model->brushq1.light_styles = 0;
2898         for (i = 0;i < 255;i++)
2899         {
2900                 if (stylecounts[i])
2901                 {
2902                         remapstyles[i] = model->brushq1.light_styles++;
2903                         totalcount += stylecounts[i] + 1;
2904                 }
2905         }
2906         if (!totalcount)
2907                 return;
2908         model->brushq1.light_style = (unsigned char *)Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(unsigned char));
2909         model->brushq1.light_stylevalue = (int *)Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2910         model->brushq1.light_styleupdatechains = (msurface_t ***)Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2911         model->brushq1.light_styleupdatechainsbuffer = (msurface_t **)Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2912         model->brushq1.light_styles = 0;
2913         for (i = 0;i < 255;i++)
2914                 if (stylecounts[i])
2915                         model->brushq1.light_style[model->brushq1.light_styles++] = i;
2916         j = 0;
2917         for (i = 0;i < model->brushq1.light_styles;i++)
2918         {
2919                 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2920                 j += stylecounts[model->brushq1.light_style[i]] + 1;
2921         }
2922         for (i = 0;i < model->nummodelsurfaces;i++)
2923         {
2924                 surface = model->data_surfaces + model->firstmodelsurface + i;
2925                 for (j = 0;j < MAXLIGHTMAPS;j++)
2926                         if (surface->lightmapinfo->styles[j] != 255)
2927                                 *model->brushq1.light_styleupdatechains[remapstyles[surface->lightmapinfo->styles[j]]]++ = surface;
2928         }
2929         j = 0;
2930         for (i = 0;i < model->brushq1.light_styles;i++)
2931         {
2932                 *model->brushq1.light_styleupdatechains[i] = NULL;
2933                 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2934                 j += stylecounts[model->brushq1.light_style[i]] + 1;
2935         }
2936 }
2937
2938 //Returns PVS data for a given point
2939 //(note: can return NULL)
2940 static unsigned char *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p)
2941 {
2942         mnode_t *node;
2943         node = model->brush.data_nodes;
2944         while (node->plane)
2945                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
2946         if (((mleaf_t *)node)->clusterindex >= 0)
2947                 return model->brush.data_pvsclusters + ((mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
2948         else
2949                 return NULL;
2950 }
2951
2952 static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, unsigned char *pvsbuffer, int pvsbytes, mnode_t *node)
2953 {
2954         while (node->plane)
2955         {
2956                 float d = PlaneDiff(org, node->plane);
2957                 if (d > radius)
2958                         node = node->children[0];
2959                 else if (d < -radius)
2960                         node = node->children[1];
2961                 else
2962                 {
2963                         // go down both sides
2964                         Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
2965                         node = node->children[1];
2966                 }
2967         }
2968         // if this leaf is in a cluster, accumulate the pvs bits
2969         if (((mleaf_t *)node)->clusterindex >= 0)
2970         {
2971                 int i;
2972                 unsigned char *pvs = model->brush.data_pvsclusters + ((mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
2973                 for (i = 0;i < pvsbytes;i++)
2974                         pvsbuffer[i] |= pvs[i];
2975         }
2976 }
2977
2978 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
2979 //of the given point.
2980 static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, unsigned char *pvsbuffer, int pvsbufferlength)
2981 {
2982         int bytes = model->brush.num_pvsclusterbytes;
2983         bytes = min(bytes, pvsbufferlength);
2984         if (r_novis.integer || !model->brush.num_pvsclusters || !Mod_Q1BSP_GetPVS(model, org))
2985         {
2986                 memset(pvsbuffer, 0xFF, bytes);
2987                 return bytes;
2988         }
2989         memset(pvsbuffer, 0, bytes);
2990         Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brush.data_nodes);
2991         return bytes;
2992 }
2993
2994 static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs)
2995 {
2996         vec3_t size;
2997         const hull_t *hull;
2998
2999         VectorSubtract(inmaxs, inmins, size);
3000         if (cmodel->brush.ismcbsp)
3001         {
3002                 if (size[0] < 3)
3003                         hull = &cmodel->brushq1.hulls[0]; // 0x0x0
3004                 else if (size[2] < 48) // pick the nearest of 40 or 56
3005                         hull = &cmodel->brushq1.hulls[2]; // 16x16x40
3006                 else
3007                         hull = &cmodel->brushq1.hulls[1]; // 16x16x56
3008         }
3009         else if (cmodel->brush.ishlbsp)
3010         {
3011                 if (size[0] < 3)
3012                         hull = &cmodel->brushq1.hulls[0]; // 0x0x0
3013                 else if (size[0] <= 32)
3014                 {
3015                         if (size[2] < 54) // pick the nearest of 36 or 72
3016                                 hull = &cmodel->brushq1.hulls[3]; // 32x32x36
3017                         else
3018                                 hull = &cmodel->brushq1.hulls[1]; // 32x32x72
3019                 }
3020                 else
3021                         hull = &cmodel->brushq1.hulls[2]; // 64x64x64
3022         }
3023         else
3024         {
3025                 if (size[0] < 3)
3026                         hull = &cmodel->brushq1.hulls[0]; // 0x0x0
3027                 else if (size[0] <= 32)
3028                         hull = &cmodel->brushq1.hulls[1]; // 32x32x56
3029                 else
3030                         hull = &cmodel->brushq1.hulls[2]; // 64x64x88
3031         }
3032         VectorCopy(inmins, outmins);
3033         VectorAdd(inmins, hull->clip_size, outmaxs);
3034 }
3035
3036 void Mod_Q1BSP_Load(model_t *mod, void *buffer, void *bufferend)
3037 {
3038         int i, j, k;
3039         dheader_t *header;
3040         dmodel_t *bm;
3041         mempool_t *mainmempool;
3042         float dist, modelyawradius, modelradius, *vec;
3043         msurface_t *surface;
3044         int numshadowmeshtriangles;
3045         dheader_t _header;
3046         hullinfo_t hullinfo;
3047
3048         mod->type = mod_brushq1;
3049
3050         if (!memcmp (buffer, "MCBSPpad", 8))
3051         {
3052                 unsigned char   *index;
3053
3054                 mod->brush.ismcbsp = true;
3055                 mod->brush.ishlbsp = false;
3056
3057                 mod_base = (unsigned char*)buffer;
3058
3059                 index = mod_base;
3060                 index += 8;
3061                 i = SB_ReadInt (&index);
3062                 if (i != MCBSPVERSION)
3063                         Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i)", mod->name, i, MCBSPVERSION);
3064
3065         // read hull info
3066                 hullinfo.numhulls = LittleLong(*(int*)index); index += 4;
3067                 hullinfo.filehulls = hullinfo.numhulls;
3068                 VectorClear (hullinfo.hullsizes[0][0]);
3069                 VectorClear (hullinfo.hullsizes[0][1]);
3070                 for (i = 1; i < hullinfo.numhulls; i++)
3071                 {
3072                         hullinfo.hullsizes[i][0][0] = SB_ReadFloat (&index);
3073                         hullinfo.hullsizes[i][0][1] = SB_ReadFloat (&index);
3074                         hullinfo.hullsizes[i][0][2] = SB_ReadFloat (&index);
3075                         hullinfo.hullsizes[i][1][0] = SB_ReadFloat (&index);
3076                         hullinfo.hullsizes[i][1][1] = SB_ReadFloat (&index);
3077                         hullinfo.hullsizes[i][1][2] = SB_ReadFloat (&index);
3078                 }
3079
3080         // read lumps
3081                 _header.version = 0;
3082                 for (i = 0; i < HEADER_LUMPS; i++)
3083                 {
3084                         _header.lumps[i].fileofs = SB_ReadInt (&index);
3085                         _header.lumps[i].filelen = SB_ReadInt (&index);
3086                 }
3087
3088                 header = &_header;
3089         }
3090         else
3091         {
3092                 header = (dheader_t *)buffer;
3093
3094                 i = LittleLong(header->version);
3095                 if (i != BSPVERSION && i != 30)
3096                         Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife)", mod->name, i, BSPVERSION);
3097                 mod->brush.ishlbsp = i == 30;
3098                 mod->brush.ismcbsp = false;
3099
3100         // fill in hull info
3101                 VectorClear (hullinfo.hullsizes[0][0]);
3102                 VectorClear (hullinfo.hullsizes[0][1]);
3103                 if (mod->brush.ishlbsp)
3104                 {
3105                         hullinfo.numhulls = 4;
3106                         hullinfo.filehulls = 4;
3107                         VectorSet (hullinfo.hullsizes[1][0], -16, -16, -36);
3108                         VectorSet (hullinfo.hullsizes[1][1], 16, 16, 36);
3109                         VectorSet (hullinfo.hullsizes[2][0], -32, -32, -32);
3110                         VectorSet (hullinfo.hullsizes[2][1], 32, 32, 32);
3111                         VectorSet (hullinfo.hullsizes[3][0], -16, -16, -18);
3112                         VectorSet (hullinfo.hullsizes[3][1], 16, 16, 18);
3113                 }
3114                 else
3115                 {
3116                         hullinfo.numhulls = 3;
3117                         hullinfo.filehulls = 4;
3118                         VectorSet (hullinfo.hullsizes[1][0], -16, -16, -24);
3119                         VectorSet (hullinfo.hullsizes[1][1], 16, 16, 32);
3120                         VectorSet (hullinfo.hullsizes[2][0], -32, -32, -24);
3121                         VectorSet (hullinfo.hullsizes[2][1], 32, 32, 64);
3122                 }
3123
3124         // read lumps
3125                 mod_base = (unsigned char*)buffer;
3126                 for (i = 0; i < HEADER_LUMPS; i++)
3127                 {
3128                         header->lumps[i].fileofs = LittleLong(header->lumps[i].fileofs);
3129                         header->lumps[i].filelen = LittleLong(header->lumps[i].filelen);
3130                 }
3131         }
3132
3133         mod->soundfromcenter = true;
3134         mod->TraceBox = Mod_Q1BSP_TraceBox;
3135         mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents;
3136         mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents;
3137         mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
3138         mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
3139         mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
3140         mod->brush.BoxTouchingLeafPVS = Mod_Q1BSP_BoxTouchingLeafPVS;
3141         mod->brush.BoxTouchingVisibleLeafs = Mod_Q1BSP_BoxTouchingVisibleLeafs;
3142         mod->brush.FindBoxClusters = Mod_Q1BSP_FindBoxClusters;
3143         mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
3144         mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
3145         mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint;
3146         mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize;
3147         mod->brush.PointInLeaf = Mod_Q1BSP_PointInLeaf;
3148
3149         if (loadmodel->isworldmodel)
3150         {
3151                 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3152                 Cvar_SetValue("mcbsp", mod->brush.ismcbsp);
3153         }
3154
3155 // load into heap
3156
3157         // store which lightmap format to use
3158         mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3159
3160         mod->brush.qw_md4sum = 0;
3161         mod->brush.qw_md4sum2 = 0;
3162         for (i = 0;i < HEADER_LUMPS;i++)
3163         {
3164                 if (i == LUMP_ENTITIES)
3165                         continue;
3166                 mod->brush.qw_md4sum ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen);
3167                 if (i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES)
3168                         continue;
3169                 mod->brush.qw_md4sum2 ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen);
3170         }
3171
3172         Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
3173         Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
3174         Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
3175         Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
3176         Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
3177         Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
3178         Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
3179         Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
3180         Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
3181         Mod_Q1BSP_LoadLeaffaces(&header->lumps[LUMP_MARKSURFACES]);
3182         Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
3183         // load submodels before leafs because they contain the number of vis leafs
3184         Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS], &hullinfo);
3185         Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
3186         Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
3187         Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES], &hullinfo);
3188
3189         if (!mod->brushq1.lightdata)
3190                 mod->brush.LightPoint = NULL;
3191
3192         if (mod->brushq1.data_compressedpvs)
3193                 Mem_Free(mod->brushq1.data_compressedpvs);
3194         mod->brushq1.data_compressedpvs = NULL;
3195         mod->brushq1.num_compressedpvs = 0;
3196
3197         Mod_Q1BSP_MakeHull0();
3198         Mod_Q1BSP_MakePortals();
3199
3200         mod->numframes = 2;             // regular and alternate animation
3201         mod->numskins = 1;
3202
3203         mainmempool = mod->mempool;
3204
3205         Mod_Q1BSP_LoadLightList();
3206
3207         // make a single combined shadow mesh to allow optimized shadow volume creation
3208         numshadowmeshtriangles = 0;
3209         for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
3210         {
3211                 surface->num_firstshadowmeshtriangle = numshadowmeshtriangles;
3212                 numshadowmeshtriangles += surface->num_triangles;
3213         }
3214         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
3215         for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
3216                 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));
3217         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true);
3218         Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
3219
3220         if (loadmodel->brush.numsubmodels)
3221                 loadmodel->brush.submodels = (model_t **)Mem_Alloc(loadmodel->mempool, loadmodel->brush.numsubmodels * sizeof(model_t *));
3222
3223         if (loadmodel->isworldmodel)
3224         {
3225                 // clear out any stale submodels or worldmodels lying around
3226                 // if we did this clear before now, an error might abort loading and
3227                 // leave things in a bad state
3228                 Mod_RemoveStaleWorldModels(loadmodel);
3229         }
3230
3231         // LordHavoc: to clear the fog around the original quake submodel code, I
3232         // will explain:
3233         // first of all, some background info on the submodels:
3234         // model 0 is the map model (the world, named maps/e1m1.bsp for example)
3235         // model 1 and higher are submodels (doors and the like, named *1, *2, etc)
3236         // now the weird for loop itself:
3237         // the loop functions in an odd way, on each iteration it sets up the
3238         // current 'mod' model (which despite the confusing code IS the model of
3239         // the number i), at the end of the loop it duplicates the model to become
3240         // the next submodel, and loops back to set up the new submodel.
3241
3242         // LordHavoc: now the explanation of my sane way (which works identically):
3243         // set up the world model, then on each submodel copy from the world model
3244         // and set up the submodel with the respective model info.
3245         for (i = 0;i < mod->brush.numsubmodels;i++)
3246         {
3247                 // LordHavoc: this code was originally at the end of this loop, but
3248                 // has been transformed to something more readable at the start here.
3249
3250                 if (i > 0)
3251                 {
3252                         char name[10];
3253                         // LordHavoc: only register submodels if it is the world
3254                         // (prevents external bsp models from replacing world submodels with
3255                         //  their own)
3256                         if (!loadmodel->isworldmodel)
3257                                 continue;
3258                         // duplicate the basic information
3259                         sprintf(name, "*%i", i);
3260                         mod = Mod_FindName(name);
3261                         // copy the base model to this one
3262                         *mod = *loadmodel;
3263                         // rename the clone back to its proper name
3264                         strcpy(mod->name, name);
3265                         // textures and memory belong to the main model
3266                         mod->texturepool = NULL;
3267                         mod->mempool = NULL;
3268                 }
3269
3270                 mod->brush.submodel = i;
3271
3272                 if (loadmodel->brush.submodels)
3273                         loadmodel->brush.submodels[i] = mod;
3274
3275                 bm = &mod->brushq1.submodels[i];
3276
3277                 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
3278                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
3279                 {
3280                         mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
3281                         mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
3282                 }
3283
3284                 mod->firstmodelsurface = bm->firstface;
3285                 mod->nummodelsurfaces = bm->numfaces;
3286
3287                 // make the model surface list (used by shadowing/lighting)
3288                 mod->surfacelist = (int *)Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->surfacelist));
3289                 for (j = 0;j < mod->nummodelsurfaces;j++)
3290                         mod->surfacelist[j] = mod->firstmodelsurface + j;
3291
3292                 // this gets altered below if sky is used
3293                 mod->DrawSky = NULL;
3294                 mod->Draw = R_Q1BSP_Draw;
3295                 mod->GetLightInfo = R_Q1BSP_GetLightInfo;
3296                 mod->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
3297                 mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
3298                 mod->DrawLight = R_Q1BSP_DrawLight;
3299                 if (i != 0)
3300                 {
3301                         mod->brush.GetPVS = NULL;
3302                         mod->brush.FatPVS = NULL;
3303                         mod->brush.BoxTouchingPVS = NULL;
3304                         mod->brush.BoxTouchingLeafPVS = NULL;
3305                         mod->brush.BoxTouchingVisibleLeafs = NULL;
3306                         mod->brush.FindBoxClusters = NULL;
3307                         mod->brush.LightPoint = NULL;
3308                         mod->brush.AmbientSoundLevelsForPoint = NULL;
3309                 }
3310                 Mod_Q1BSP_BuildLightmapUpdateChains(loadmodel->mempool, mod);
3311                 if (mod->nummodelsurfaces)
3312                 {
3313                         // LordHavoc: calculate bmodel bounding box rather than trusting what it says
3314                         mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
3315                         mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
3316                         modelyawradius = 0;
3317                         modelradius = 0;
3318                         for (j = 0, surface = &mod->data_surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surface++)
3319                         {
3320                                 // we only need to have a drawsky function if it is used(usually only on world model)
3321                                 if (surface->texture->basematerialflags & MATERIALFLAG_SKY)
3322                                         mod->DrawSky = R_Q1BSP_DrawSky;
3323                                 // calculate bounding shapes
3324                                 for (k = 0, vec = (surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex);k < surface->num_vertices;k++, vec += 3)
3325                                 {
3326                                         if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
3327                                         if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
3328                                         if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
3329                                         if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
3330                                         if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
3331                                         if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
3332                                         dist = vec[0]*vec[0]+vec[1]*vec[1];
3333                                         if (modelyawradius < dist)
3334                                                 modelyawradius = dist;
3335                                         dist += vec[2]*vec[2];
3336                                         if (modelradius < dist)
3337                                                 modelradius = dist;
3338                                 }
3339                         }
3340                         modelyawradius = sqrt(modelyawradius);
3341                         modelradius = sqrt(modelradius);
3342                         mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
3343                         mod->yawmins[2] = mod->normalmins[2];
3344                         mod->yawmaxs[2] = mod->normalmaxs[2];
3345                         mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
3346                         mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
3347                         mod->radius = modelradius;
3348                         mod->radius2 = modelradius * modelradius;
3349                 }
3350                 else
3351                 {
3352                         // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
3353                         Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadmodel->name);
3354                 }
3355                 //mod->brushq1.num_visleafs = bm->visleafs;
3356         }
3357
3358         Mod_Q1BSP_LoadMapBrushes();
3359
3360         //Mod_Q1BSP_ProcessLightList();
3361
3362         if (developer.integer)
3363                 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);
3364 }
3365
3366 static void Mod_Q2BSP_LoadEntities(lump_t *l)
3367 {
3368 }
3369
3370 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
3371 {
3372 /*
3373         d_t *in;
3374         m_t *out;
3375         int i, count;
3376
3377         in = (void *)(mod_base + l->fileofs);
3378         if (l->filelen % sizeof(*in))
3379                 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3380         count = l->filelen / sizeof(*in);
3381         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3382
3383         loadmodel-> = out;
3384         loadmodel->num = count;
3385
3386         for (i = 0;i < count;i++, in++, out++)
3387         {
3388         }
3389 */
3390 }
3391
3392 static void Mod_Q2BSP_LoadVertices(lump_t *l)
3393 {
3394 /*
3395         d_t *in;
3396         m_t *out;
3397         int i, count;
3398
3399         in = (void *)(mod_base + l->fileofs);
3400         if (l->filelen % sizeof(*in))
3401                 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3402         count = l->filelen / sizeof(*in);
3403         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3404
3405         loadmodel-> = out;
3406         loadmodel->num = count;
3407
3408         for (i = 0;i < count;i++, in++, out++)
3409         {
3410         }
3411 */
3412 }
3413
3414 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
3415 {
3416 /*
3417         d_t *in;
3418         m_t *out;
3419         int i, count;
3420
3421         in = (void *)(mod_base + l->fileofs);
3422         if (l->filelen % sizeof(*in))
3423                 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
3424         count = l->filelen / sizeof(*in);
3425         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3426
3427         loadmodel-> = out;
3428         loadmodel->num = count;
3429
3430         for (i = 0;i < count;i++, in++, out++)
3431         {
3432         }
3433 */
3434 }
3435
3436 static void Mod_Q2BSP_LoadNodes(lump_t *l)
3437 {
3438 /*
3439         d_t *in;
3440         m_t *out;
3441         int i, count;
3442
3443         in = (void *)(mod_base + l->fileofs);
3444         if (l->filelen % sizeof(*in))
3445                 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3446         count = l->filelen / sizeof(*in);
3447         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3448
3449         loadmodel-> = out;
3450         loadmodel->num = count;
3451
3452         for (i = 0;i < count;i++, in++, out++)
3453         {
3454         }
3455 */
3456 }
3457
3458 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
3459 {
3460 /*
3461         d_t *in;
3462         m_t *out;
3463         int i, count;
3464
3465         in = (void *)(mod_base + l->fileofs);
3466         if (l->filelen % sizeof(*in))
3467                 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
3468         count = l->filelen / sizeof(*in);
3469         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3470
3471         loadmodel-> = out;
3472         loadmodel->num = count;
3473
3474         for (i = 0;i < count;i++, in++, out++)
3475         {
3476         }
3477 */
3478 }
3479
3480 static void Mod_Q2BSP_LoadFaces(lump_t *l)
3481 {
3482 /*
3483         d_t *in;
3484         m_t *out;
3485         int i, count;
3486
3487         in = (void *)(mod_base + l->fileofs);
3488         if (l->filelen % sizeof(*in))
3489                 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3490         count = l->filelen / sizeof(*in);
3491         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3492
3493         loadmodel-> = out;
3494         loadmodel->num = count;
3495
3496         for (i = 0;i < count;i++, in++, out++)
3497         {
3498         }
3499 */
3500 }
3501
3502 static void Mod_Q2BSP_LoadLighting(lump_t *l)
3503 {
3504 /*
3505         d_t *in;
3506         m_t *out;
3507         int i, count;
3508
3509         in = (void *)(mod_base + l->fileofs);
3510         if (l->filelen % sizeof(*in))
3511                 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
3512         count = l->filelen / sizeof(*in);
3513         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3514
3515         loadmodel-> = out;
3516         loadmodel->num = count;
3517
3518         for (i = 0;i < count;i++, in++, out++)
3519         {
3520         }
3521 */
3522 }
3523
3524 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
3525 {
3526 /*
3527         d_t *in;
3528         m_t *out;
3529         int i, count;
3530
3531         in = (void *)(mod_base + l->fileofs);
3532         if (l->filelen % sizeof(*in))
3533                 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3534         count = l->filelen / sizeof(*in);
3535         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3536
3537         loadmodel-> = out;
3538         loadmodel->num = count;
3539
3540         for (i = 0;i < count;i++, in++, out++)
3541         {
3542         }
3543 */
3544 }
3545
3546 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3547 {
3548 /*
3549         d_t *in;
3550         m_t *out;
3551         int i, count;
3552
3553         in = (void *)(mod_base + l->fileofs);
3554         if (l->filelen % sizeof(*in))
3555                 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3556         count = l->filelen / sizeof(*in);
3557         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3558
3559         loadmodel-> = out;
3560         loadmodel->num = count;
3561
3562         for (i = 0;i < count;i++, in++, out++)
3563         {
3564         }
3565 */
3566 }
3567
3568 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3569 {
3570 /*
3571         d_t *in;
3572         m_t *out;
3573         int i, count;
3574
3575         in = (void *)(mod_base + l->fileofs);
3576         if (l->filelen % sizeof(*in))
3577                 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3578         count = l->filelen / sizeof(*in);
3579         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3580
3581         loadmodel-> = out;
3582         loadmodel->num = count;
3583
3584         for (i = 0;i < count;i++, in++, out++)
3585         {
3586         }
3587 */
3588 }
3589
3590 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3591 {
3592 /*
3593         d_t *in;
3594         m_t *out;
3595         int i, count;
3596
3597         in = (void *)(mod_base + l->fileofs);
3598         if (l->filelen % sizeof(*in))
3599                 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3600         count = l->filelen / sizeof(*in);
3601         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3602
3603         loadmodel-> = out;
3604         loadmodel->num = count;
3605
3606         for (i = 0;i < count;i++, in++, out++)
3607         {
3608         }
3609 */
3610 }
3611
3612 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3613 {
3614 /*
3615         d_t *in;
3616         m_t *out;
3617         int i, count;
3618
3619         in = (void *)(mod_base + l->fileofs);
3620         if (l->filelen % sizeof(*in))
3621                 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3622         count = l->filelen / sizeof(*in);
3623         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3624
3625         loadmodel-> = out;
3626         loadmodel->num = count;
3627
3628         for (i = 0;i < count;i++, in++, out++)
3629         {
3630         }
3631 */
3632 }
3633
3634 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3635 {
3636 /*
3637         d_t *in;
3638         m_t *out;
3639         int i, count;
3640
3641         in = (void *)(mod_base + l->fileofs);
3642         if (l->filelen % sizeof(*in))
3643                 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3644         count = l->filelen / sizeof(*in);
3645         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3646
3647         loadmodel-> = out;
3648         loadmodel->num = count;
3649
3650         for (i = 0;i < count;i++, in++, out++)
3651         {
3652         }
3653 */
3654 }
3655
3656 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3657 {
3658 /*
3659         d_t *in;
3660         m_t *out;
3661         int i, count;
3662
3663         in = (void *)(mod_base + l->fileofs);
3664         if (l->filelen % sizeof(*in))
3665                 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3666         count = l->filelen / sizeof(*in);
3667         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3668
3669         loadmodel-> = out;
3670         loadmodel->num = count;
3671
3672         for (i = 0;i < count;i++, in++, out++)
3673         {
3674         }
3675 */
3676 }
3677
3678 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3679 {
3680 /*
3681         d_t *in;
3682         m_t *out;
3683         int i, count;
3684
3685         in = (void *)(mod_base + l->fileofs);
3686         if (l->filelen % sizeof(*in))
3687                 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3688         count = l->filelen / sizeof(*in);
3689         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3690
3691         loadmodel-> = out;
3692         loadmodel->num = count;
3693
3694         for (i = 0;i < count;i++, in++, out++)
3695         {
3696         }
3697 */
3698 }
3699
3700 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3701 {
3702 /*
3703         d_t *in;
3704         m_t *out;
3705         int i, count;
3706
3707         in = (void *)(mod_base + l->fileofs);
3708         if (l->filelen % sizeof(*in))
3709                 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3710         count = l->filelen / sizeof(*in);
3711         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3712
3713         loadmodel-> = out;
3714         loadmodel->num = count;
3715
3716         for (i = 0;i < count;i++, in++, out++)
3717         {
3718         }
3719 */
3720 }
3721
3722 static void Mod_Q2BSP_LoadModels(lump_t *l)
3723 {
3724 /*
3725         d_t *in;
3726         m_t *out;
3727         int i, count;
3728
3729         in = (void *)(mod_base + l->fileofs);
3730         if (l->filelen % sizeof(*in))
3731                 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3732         count = l->filelen / sizeof(*in);
3733         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3734
3735         loadmodel-> = out;
3736         loadmodel->num = count;
3737
3738         for (i = 0;i < count;i++, in++, out++)
3739         {
3740         }
3741 */
3742 }
3743
3744 void static Mod_Q2BSP_Load(model_t *mod, void *buffer, void *bufferend)
3745 {
3746         int i;
3747         q2dheader_t *header;
3748
3749         Host_Error("Mod_Q2BSP_Load: not yet implemented");
3750
3751         mod->type = mod_brushq2;
3752
3753         header = (q2dheader_t *)buffer;
3754
3755         i = LittleLong(header->version);
3756         if (i != Q2BSPVERSION)
3757                 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3758         mod->brush.ishlbsp = false;
3759         mod->brush.ismcbsp = false;
3760         if (loadmodel->isworldmodel)
3761         {
3762                 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3763                 Cvar_SetValue("mcbsp", mod->brush.ismcbsp);
3764         }
3765
3766         mod_base = (unsigned char *)header;
3767
3768         // swap all the lumps
3769         for (i = 0;i < (int) sizeof(*header) / 4;i++)
3770                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3771
3772         // store which lightmap format to use
3773         mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3774
3775         mod->brush.qw_md4sum = 0;
3776         mod->brush.qw_md4sum2 = 0;
3777         for (i = 0;i < Q2HEADER_LUMPS;i++)
3778         {
3779                 if (i == Q2LUMP_ENTITIES)
3780                         continue;
3781                 mod->brush.qw_md4sum ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen);
3782                 if (i == Q2LUMP_VISIBILITY || i == Q2LUMP_LEAFS || i == Q2LUMP_NODES)
3783                         continue;
3784                 mod->brush.qw_md4sum2 ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen);
3785         }
3786
3787         Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3788         Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3789         Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3790         Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3791         Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3792         Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3793         Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3794         Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3795         Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3796         Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3797         Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3798         Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3799         Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3800         Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3801         Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3802         Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3803         Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3804         // LordHavoc: must go last because this makes the submodels
3805         Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3806 }
3807
3808 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents);
3809 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents);
3810
3811 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3812 {
3813         const char *data;
3814         char key[128], value[MAX_INPUTLINE];
3815         float v[3];
3816         loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3817         loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3818         loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3819         if (!l->filelen)
3820                 return;
3821         loadmodel->brush.entities = (char *)Mem_Alloc(loadmodel->mempool, l->filelen);
3822         memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3823         data = loadmodel->brush.entities;
3824         // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3825         if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3826         {
3827                 while (1)
3828                 {
3829                         if (!COM_ParseToken(&data, false))
3830                                 break; // error
3831                         if (com_token[0] == '}')
3832                                 break; // end of worldspawn
3833                         if (com_token[0] == '_')
3834                                 strcpy(key, com_token + 1);
3835                         else
3836                                 strcpy(key, com_token);
3837                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
3838                                 key[strlen(key)-1] = 0;
3839                         if (!COM_ParseToken(&data, false))
3840                                 break; // error
3841                         strcpy(value, com_token);
3842                         if (!strcmp("gridsize", key))
3843                         {
3844                                 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3845                                         VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3846                         }
3847                 }
3848         }
3849 }
3850
3851 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3852 {
3853         q3dtexture_t *in;
3854         texture_t *out;
3855         int i, count;
3856         int j, c;
3857         fssearch_t *search;
3858         char *f;
3859         const char *text;
3860         int flags, flags2, numparameters, passnumber;
3861         char shadername[Q3PATHLENGTH];
3862         char sky[Q3PATHLENGTH];
3863         char firstpasstexturename[Q3PATHLENGTH];
3864         char parameter[4][Q3PATHLENGTH];
3865
3866         in = (q3dtexture_t *)(mod_base + l->fileofs);
3867         if (l->filelen % sizeof(*in))
3868                 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3869         count = l->filelen / sizeof(*in);
3870         out = (texture_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3871
3872         loadmodel->data_textures = out;
3873         loadmodel->num_textures = count;
3874
3875         for (i = 0;i < count;i++, in++, out++)
3876         {
3877                 strlcpy (out->name, in->name, sizeof (out->name));
3878                 out->surfaceflags = LittleLong(in->surfaceflags);
3879                 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, LittleLong(in->contents));
3880                 out->surfaceparms = -1;
3881         }
3882
3883         // do a quick parse of shader files to get surfaceparms
3884         if ((search = FS_Search("scripts/*.shader", true, false)))
3885         {
3886                 for (i = 0;i < search->numfilenames;i++)
3887                 {
3888                         if ((f = (char *)FS_LoadFile(search->filenames[i], tempmempool, false, NULL)))
3889                         {
3890                                 text = f;
3891                                 while (COM_ParseToken(&text, false))
3892                                 {
3893                                         strlcpy (shadername, com_token, sizeof (shadername));
3894                                         flags = 0;
3895                                         flags2 = 0;
3896                                         sky[0] = 0;
3897                                         passnumber = 0;
3898                                         firstpasstexturename[0] = 0;
3899                                         if (COM_ParseToken(&text, false) && !strcasecmp(com_token, "{"))
3900                                         {
3901                                                 while (COM_ParseToken(&text, false))
3902                                                 {
3903                                                         if (!strcasecmp(com_token, "}"))
3904                                                                 break;
3905                                                         else if (!strcasecmp(com_token, "{"))
3906                                                         {
3907                                                                 while (COM_ParseToken(&text, false))
3908                                                                 {
3909                                                                         if (!strcasecmp(com_token, "}"))
3910                                                                                 break;
3911                                                                         if (!strcasecmp(com_token, "\n"))
3912                                                                                 continue;
3913                                                                         numparameters = 0;
3914                                                                         for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
3915                                                                         {
3916                                                                                 if (j < 4)
3917                                                                                 {
3918                                                                                         strlcpy(parameter[j], com_token, sizeof(parameter[j]));
3919                                                                                         numparameters = j + 1;
3920                                                                                 }
3921                                                                                 if (!COM_ParseToken(&text, true))
3922                                                                                         break;
3923                                                                         }
3924                                                                         if (developer.integer >= 2)
3925                                                                         {
3926                                                                                 Con_Printf("%s %i: ", shadername, passnumber);
3927                                                                                 for (j = 0;j < numparameters;j++)
3928                                                                                         Con_Printf(" %s", parameter[j]);
3929                                                                                 Con_Print("\n");
3930                                                                         }
3931                                                                         if (passnumber == 0 && numparameters >= 1)
3932                                                                         {
3933                                                                                 if (!strcasecmp(parameter[0], "blendfunc") && (flags & Q3SURFACEPARM_TRANS))
3934                                                                                 {
3935                                                                                         if (numparameters == 2 && !strcasecmp(parameter[1], "add"))
3936                                                                                                 flags2 |= Q3TEXTUREFLAG_ADDITIVE;
3937                                                                                         else if (numparameters == 3 && !strcasecmp(parameter[1], "gl_one") && !strcasecmp(parameter[2], "gl_one"))
3938                                                                                                 flags2 |= Q3TEXTUREFLAG_ADDITIVE;
3939                                                                                         else if (numparameters == 3 && !strcasecmp(parameter[1], "gl_src_alpha") && !strcasecmp(parameter[2], "gl_one"))
3940                                                                                                 flags2 |= Q3TEXTUREFLAG_ADDITIVE;
3941                                                                                 }
3942                                                                                 else if (numparameters >= 2 && (!strcasecmp(parameter[0], "map") || !strcasecmp(parameter[0], "clampmap")))
3943                                                                                         strlcpy(firstpasstexturename, parameter[1], sizeof(firstpasstexturename));
3944                                                                                 else if (numparameters >= 3 && !strcasecmp(parameter[0], "animmap"))
3945                                                                                         strlcpy(firstpasstexturename, parameter[2], sizeof(firstpasstexturename));
3946                                                                                 else if (numparameters >= 2 && !strcasecmp(parameter[0], "alphafunc"))
3947                                                                                         flags2 |= Q3TEXTUREFLAG_ALPHATEST;
3948                                                                         }
3949                                                                         // break out a level if it was }
3950                                                                         if (!strcasecmp(com_token, "}"))
3951                                                                                 break;
3952                                                                 }
3953                                                                 passnumber++;
3954                                                                 continue;
3955                                                         }
3956                                                         numparameters = 0;
3957                                                         for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
3958                                                         {
3959                                                                 if (j < 4)
3960                                                                 {
3961                                                                         strlcpy(parameter[j], com_token, sizeof(parameter[j]));
3962                                                                         numparameters = j + 1;
3963                                                                 }
3964                                                                 if (!COM_ParseToken(&text, true))
3965                                                                         break;
3966                                                         }
3967                                                         if (i == 0 && !strcasecmp(com_token, "}"))
3968                                                                 break;
3969                                                         if (developer.integer >= 2)
3970                                                         {
3971                                                                 Con_Printf("%s: ", shadername);
3972                                                                 for (j = 0;j < numparameters;j++)
3973                                                                         Con_Printf(" %s", parameter[j]);
3974                                                                 Con_Print("\n");
3975                                                         }
3976                                                         if (numparameters < 1)
3977                                                                 continue;
3978                                                         if (!strcasecmp(parameter[0], "surfaceparm") && numparameters >= 2)
3979                                                         {
3980                                                                 if (!strcasecmp(parameter[1], "alphashadow"))
3981                                                                         flags |= Q3SURFACEPARM_ALPHASHADOW;
3982                                                                 else if (!strcasecmp(parameter[1], "areaportal"))
3983                                                                         flags |= Q3SURFACEPARM_AREAPORTAL;
3984                                                                 else if (!strcasecmp(parameter[1], "clusterportal"))
3985                                                                         flags |= Q3SURFACEPARM_CLUSTERPORTAL;
3986                                                                 else if (!strcasecmp(parameter[1], "detail"))
3987                                                                         flags |= Q3SURFACEPARM_DETAIL;
3988                                                                 else if (!strcasecmp(parameter[1], "donotenter"))
3989                                                                         flags |= Q3SURFACEPARM_DONOTENTER;
3990                                                                 else if (!strcasecmp(parameter[1], "fog"))
3991                                                                         flags |= Q3SURFACEPARM_FOG;
3992                                                                 else if (!strcasecmp(parameter[1], "lava"))
3993                                                                         flags |= Q3SURFACEPARM_LAVA;
3994                                                                 else if (!strcasecmp(parameter[1], "lightfilter"))
3995                                                                         flags |= Q3SURFACEPARM_LIGHTFILTER;
3996                                                                 else if (!strcasecmp(parameter[1], "metalsteps"))
3997                                                                         flags |= Q3SURFACEPARM_METALSTEPS;
3998                                                                 else if (!strcasecmp(parameter[1], "nodamage"))
3999                                                                         flags |= Q3SURFACEPARM_NODAMAGE;
4000                                                                 else if (!strcasecmp(parameter[1], "nodlight"))
4001                                                                         flags |= Q3SURFACEPARM_NODLIGHT;
4002                                                                 else if (!strcasecmp(parameter[1], "nodraw"))
4003                                                                         flags |= Q3SURFACEPARM_NODRAW;
4004                                                                 else if (!strcasecmp(parameter[1], "nodrop"))
4005                                                                         flags |= Q3SURFACEPARM_NODROP;
4006                                                                 else if (!strcasecmp(parameter[1], "noimpact"))
4007                                                                         flags |= Q3SURFACEPARM_NOIMPACT;
4008                                                                 else if (!strcasecmp(parameter[1], "nolightmap"))
4009                                                                         flags |= Q3SURFACEPARM_NOLIGHTMAP;
4010                                                                 else if (!strcasecmp(parameter[1], "nomarks"))
4011                                                                         flags |= Q3SURFACEPARM_NOMARKS;
4012                                                                 else if (!strcasecmp(parameter[1], "nomipmaps"))
4013                                                                         flags |= Q3SURFACEPARM_NOMIPMAPS;
4014                                                                 else if (!strcasecmp(parameter[1], "nonsolid"))
4015                                                                         flags |= Q3SURFACEPARM_NONSOLID;
4016                                                                 else if (!strcasecmp(parameter[1], "origin"))
4017                                                                         flags |= Q3SURFACEPARM_ORIGIN;
4018                                                                 else if (!strcasecmp(parameter[1], "playerclip"))
4019                                                                         flags |= Q3SURFACEPARM_PLAYERCLIP;
4020                                                                 else if (!strcasecmp(parameter[1], "sky"))
4021                                                                         flags |= Q3SURFACEPARM_SKY;
4022                                                                 else if (!strcasecmp(parameter[1], "slick"))
4023                                                                         flags |= Q3SURFACEPARM_SLICK;
4024                                                                 else if (!strcasecmp(parameter[1], "slime"))
4025                                                                         flags |= Q3SURFACEPARM_SLIME;
4026                                                                 else if (!strcasecmp(parameter[1], "structural"))
4027                                                                         flags |= Q3SURFACEPARM_STRUCTURAL;
4028                                                                 else if (!strcasecmp(parameter[1], "trans"))
4029                                                                         flags |= Q3SURFACEPARM_TRANS;
4030                                                                 else if (!strcasecmp(parameter[1], "water"))
4031                                                                         flags |= Q3SURFACEPARM_WATER;
4032                                                                 else if (!strcasecmp(parameter[1], "pointlight"))
4033                                                                         flags |= Q3SURFACEPARM_POINTLIGHT;
4034                                                                 else
4035                                                                         Con_Printf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[i], parameter[1]);
4036                                                         }
4037                                                         else if (!strcasecmp(parameter[0], "sky") && numparameters >= 2)
4038                                                                 strlcpy(sky, parameter[1], sizeof(sky));
4039                                                         else if (!strcasecmp(parameter[0], "skyparms") && numparameters >= 2)
4040                                                         {
4041                                                                 if (!atoi(parameter[1]) && strcasecmp(parameter[1], "-"))
4042                                                                         strlcpy(sky, parameter[1], sizeof(sky));
4043                                                         }
4044                                                         else if (!strcasecmp(parameter[0], "cull") && numparameters >= 2)
4045                                                         {
4046                                                                 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "twosided"))
4047                                                                         flags2 |= Q3TEXTUREFLAG_TWOSIDED;
4048                                                         }
4049                                                         else if (!strcasecmp(parameter[0], "nomipmaps"))
4050                                                                 flags2 |= Q3TEXTUREFLAG_NOMIPMAPS;
4051                                                         else if (!strcasecmp(parameter[0], "nopicmip"))
4052                                                                 flags2 |= Q3TEXTUREFLAG_NOPICMIP;
4053                                                         else if (!strcasecmp(parameter[0], "deformvertexes") && numparameters >= 2)
4054                                                         {
4055                                                                 if (!strcasecmp(parameter[1], "autosprite") && numparameters == 2)
4056                                                                         flags2 |= Q3TEXTUREFLAG_AUTOSPRITE;
4057                                                                 if (!strcasecmp(parameter[1], "autosprite2") && numparameters == 2)
4058                                                                         flags2 |= Q3TEXTUREFLAG_AUTOSPRITE2;
4059                                                         }
4060                                                 }
4061                                                 // add shader to list (shadername and flags)
4062                                                 // actually here we just poke into the texture settings
4063                                                 for (j = 0, out = loadmodel->data_textures;j < loadmodel->num_textures;j++, out++)
4064                                                 {
4065                                                         if (!strcasecmp(out->name, shadername))
4066                                                         {
4067                                                                 out->surfaceparms = flags;
4068                                                                 out->textureflags = flags2;
4069                                                                 out->basematerialflags = 0;
4070                                                                 if (out->surfaceparms & Q3SURFACEPARM_NODRAW)
4071                                                                         out->basematerialflags |= MATERIALFLAG_NODRAW;
4072                                                                 else if (out->surfaceparms & Q3SURFACEPARM_SKY)
4073                                                                         out->basematerialflags |= MATERIALFLAG_SKY;
4074                                                                 else if (out->surfaceparms & Q3SURFACEPARM_LAVA)
4075                                                                         out->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_FULLBRIGHT;
4076                                                                 else if (out->surfaceparms & Q3SURFACEPARM_SLIME)
4077                                                                         out->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_WATERALPHA;
4078                                                                 else if (out->surfaceparms & Q3SURFACEPARM_WATER)
4079                                                                         out->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_WATERALPHA;
4080                                                                 else
4081                                                                         out->basematerialflags |= MATERIALFLAG_WALL;
4082                                                                 if (out->textureflags & Q3TEXTUREFLAG_ALPHATEST)
4083                                                                 {
4084                                                                         // FIXME: support alpha test?
4085                                                                         out->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_TRANSPARENT;
4086                                                                 }
4087                                                                 else if (out->surfaceparms & Q3SURFACEPARM_TRANS)
4088                                                                 {
4089                                                                         if (out->textureflags & Q3TEXTUREFLAG_ADDITIVE)
4090                                                                                 out->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_TRANSPARENT;
4091                                                                         else
4092                                                                                 out->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_TRANSPARENT;
4093                                                                 }
4094                                                                 strlcpy(out->firstpasstexturename, firstpasstexturename, sizeof(out->firstpasstexturename));
4095                                                                 if ((flags & Q3SURFACEPARM_SKY) && sky[0])
4096                                                                 {
4097                                                                         // quake3 seems to append a _ to the skybox name, so this must do so as well
4098                                                                         dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", sky);
4099                                                                 }
4100                                                         }
4101                                                 }
4102                                         }
4103                                         else
4104                                         {
4105                                                 Con_Printf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[i], com_token);
4106                                                 goto parseerror;
4107                                         }
4108                                 }
4109 parseerror:
4110                                 Mem_Free(f);
4111                         }
4112                 }
4113         }
4114
4115         c = 0;
4116         for (j = 0, out = loadmodel->data_textures;j < loadmodel->num_textures;j++, out++)
4117         {
4118                 if (out->surfaceparms == -1)
4119                 {
4120                         c++;
4121                         Con_DPrintf("%s: No shader found for texture \"%s\"\n", loadmodel->name, out->name);
4122                         out->surfaceparms = 0;
4123                         if (out->surfaceflags & Q3SURFACEFLAG_NODRAW)
4124                                 out->basematerialflags |= MATERIALFLAG_NODRAW;
4125                         else if (out->surfaceflags & Q3SURFACEFLAG_SKY)
4126                                 out->basematerialflags |= MATERIALFLAG_SKY;
4127                         else
4128                                 out->basematerialflags |= MATERIALFLAG_WALL;
4129                         // these are defaults
4130                         //if (!strncmp(out->name, "textures/skies/", 15))
4131                         //      out->surfaceparms |= Q3SURFACEPARM_SKY;
4132                         //if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk")
4133                         // || !strcmp(out->name, "nodraw") || !strcmp(out->name, "common/nodraw") || !strcmp(out->name, "textures/common/nodraw"))
4134                         //      out->surfaceparms |= Q3SURFACEPARM_NODRAW;
4135                         //if (R_TextureHasAlpha(out->skin.base))
4136                         //      out->surfaceparms |= Q3SURFACEPARM_TRANS;
4137                 }
4138                 if (cls.state != ca_dedicated)
4139                         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))
4140                                 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))
4141                                         Con_Printf("%s: texture loading for shader \"%s\" failed (first layer \"%s\" not found either)\n", loadmodel->name, out->name, out->firstpasstexturename);
4142                 // no animation
4143                 out->currentframe = out;
4144         }
4145         if (c)
4146                 Con_DPrintf("%s: %i textures missing shaders\n", loadmodel->name, c);
4147 }
4148
4149 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
4150 {
4151         q3dplane_t *in;
4152         mplane_t *out;
4153         int i, count;
4154
4155         in = (q3dplane_t *)(mod_base + l->fileofs);
4156         if (l->filelen % sizeof(*in))
4157                 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
4158         count = l->filelen / sizeof(*in);
4159         out = (mplane_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4160
4161         loadmodel->brush.data_planes = out;
4162         loadmodel->brush.num_planes = count;
4163
4164         for (i = 0;i < count;i++, in++, out++)
4165         {
4166                 out->normal[0] = LittleFloat(in->normal[0]);
4167                 out->normal[1] = LittleFloat(in->normal[1]);
4168                 out->normal[2] = LittleFloat(in->normal[2]);
4169                 out->dist = LittleFloat(in->dist);
4170                 PlaneClassify(out);
4171         }
4172 }
4173
4174 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
4175 {
4176         q3dbrushside_t *in;
4177         q3mbrushside_t *out;
4178         int i, n, count;
4179
4180         in = (q3dbrushside_t *)(mod_base + l->fileofs);
4181         if (l->filelen % sizeof(*in))
4182                 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
4183         count = l->filelen / sizeof(*in);
4184         out = (q3mbrushside_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4185
4186         loadmodel->brush.data_brushsides = out;
4187         loadmodel->brush.num_brushsides = count;
4188
4189         for (i = 0;i < count;i++, in++, out++)
4190         {
4191                 n = LittleLong(in->planeindex);
4192                 if (n < 0 || n >= loadmodel->brush.num_planes)
4193                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)", n, loadmodel->brush.num_planes);
4194                 out->plane = loadmodel->brush.data_planes + n;
4195                 n = LittleLong(in->textureindex);
4196                 if (n < 0 || n >= loadmodel->num_textures)
4197                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)", n, loadmodel->num_textures);
4198                 out->texture = loadmodel->data_textures + n;
4199         }
4200 }
4201
4202 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
4203 {
4204         q3dbrush_t *in;
4205         q3mbrush_t *out;
4206         int i, j, n, c, count, maxplanes;
4207         mplane_t *planes;
4208
4209         in = (q3dbrush_t *)(mod_base + l->fileofs);
4210         if (l->filelen % sizeof(*in))
4211                 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
4212         count = l->filelen / sizeof(*in);
4213         out = (q3mbrush_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4214
4215         loadmodel->brush.data_brushes = out;
4216         loadmodel->brush.num_brushes = count;
4217
4218         maxplanes = 0;
4219         planes = NULL;
4220
4221         for (i = 0;i < count;i++, in++, out++)
4222         {
4223                 n = LittleLong(in->firstbrushside);
4224                 c = LittleLong(in->numbrushsides);
4225                 if (n < 0 || n + c > loadmodel->brush.num_brushsides)
4226                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)", n, n + c, loadmodel->brush.num_brushsides);
4227                 out->firstbrushside = loadmodel->brush.data_brushsides + n;
4228                 out->numbrushsides = c;
4229                 n = LittleLong(in->textureindex);
4230                 if (n < 0 || n >= loadmodel->num_textures)
4231                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)", n, loadmodel->num_textures);
4232                 out->texture = loadmodel->data_textures + n;
4233
4234                 // make a list of mplane_t structs to construct a colbrush from
4235                 if (maxplanes < out->numbrushsides)
4236                 {
4237                         maxplanes = out->numbrushsides;
4238                         if (planes)
4239                                 Mem_Free(planes);
4240                         planes = (mplane_t *)Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
4241                 }
4242                 for (j = 0;j < out->numbrushsides;j++)
4243                 {
4244                         VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
4245                         planes[j].dist = out->firstbrushside[j].plane->dist;
4246                 }
4247                 // make the colbrush from the planes
4248                 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents);
4249         }
4250         if (planes)
4251                 Mem_Free(planes);
4252 }
4253
4254 static void Mod_Q3BSP_LoadEffects(lump_t *l)
4255 {
4256         q3deffect_t *in;
4257         q3deffect_t *out;
4258         int i, n, count;
4259
4260         in = (q3deffect_t *)(mod_base + l->fileofs);
4261         if (l->filelen % sizeof(*in))
4262                 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
4263         count = l->filelen / sizeof(*in);
4264         out = (q3deffect_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4265
4266         loadmodel->brushq3.data_effects = out;
4267         loadmodel->brushq3.num_effects = count;
4268
4269         for (i = 0;i < count;i++, in++, out++)
4270         {
4271                 strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
4272                 n = LittleLong(in->brushindex);
4273                 if (n >= loadmodel->brush.num_brushes)
4274                 {
4275                         Con_Printf("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes), setting to -1\n", n, loadmodel->brush.num_brushes);
4276                         n = -1;
4277                 }
4278                 out->brushindex = n;
4279                 out->unknown = LittleLong(in->unknown);
4280         }
4281 }
4282
4283 static void Mod_Q3BSP_LoadVertices(lump_t *l)
4284 {
4285         q3dvertex_t *in;
4286         int i, count;
4287
4288         in = (q3dvertex_t *)(mod_base + l->fileofs);
4289         if (l->filelen % sizeof(*in))
4290                 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
4291         loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
4292         loadmodel->brushq3.data_vertex3f = (float *)Mem_Alloc(loadmodel->mempool, count * (sizeof(float) * (3 + 2 + 2 + 4)));
4293         loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + count * 3;
4294         loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + count * 2;
4295         loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_texcoordlightmap2f + count * 2;
4296
4297         for (i = 0;i < count;i++, in++)
4298         {
4299                 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
4300                 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
4301                 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
4302                 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
4303                 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
4304                 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
4305                 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
4306                 // svector/tvector are calculated later in face loading
4307                 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
4308                 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
4309                 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
4310                 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
4311         }
4312 }
4313
4314 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
4315 {
4316         int *in;
4317         int *out;
4318         int i, count;
4319
4320         in = (int *)(mod_base + l->fileofs);
4321         if (l->filelen % sizeof(int[3]))
4322                 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
4323         count = l->filelen / sizeof(*in);
4324         out = (int *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4325
4326         loadmodel->brushq3.num_triangles = count / 3;
4327         loadmodel->brushq3.data_element3i = out;
4328
4329         for (i = 0;i < count;i++, in++, out++)
4330         {
4331                 *out = LittleLong(*in);
4332                 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
4333                 {
4334                         Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
4335                         *out = 0;
4336                 }
4337         }
4338 }
4339
4340 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
4341 {
4342         q3dlightmap_t *in;
4343         rtexture_t **out;
4344         int i, count;
4345
4346         if (!l->filelen)
4347                 return;
4348         in = (q3dlightmap_t *)(mod_base + l->fileofs);
4349         if (l->filelen % sizeof(*in))
4350                 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
4351         count = l->filelen / sizeof(*in);
4352         out = (rtexture_t **)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4353
4354         loadmodel->brushq3.data_lightmaps = out;
4355         loadmodel->brushq3.num_lightmaps = count;
4356
4357         for (i = 0;i < count;i++, in++, out++)
4358                 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
4359 }
4360
4361 static void Mod_Q3BSP_LoadFaces(lump_t *l)
4362 {
4363         q3dface_t *in, *oldin;
4364         msurface_t *out, *oldout;
4365         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;
4366         //int *originalelement3i;
4367         //int *originalneighbor3i;
4368         float *originalvertex3f;
4369         //float *originalsvector3f;
4370         //float *originaltvector3f;
4371         //float *originalnormal3f;
4372         float *originalcolor4f;
4373         float *originaltexcoordtexture2f;
4374         float *originaltexcoordlightmap2f;
4375         float *v;
4376         surfmesh_t *mesh, *tempmeshlist[1024];
4377
4378         in = (q3dface_t *)(mod_base + l->fileofs);
4379         if (l->filelen % sizeof(*in))
4380                 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
4381         count = l->filelen / sizeof(*in);
4382         out = (msurface_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4383
4384         loadmodel->data_surfaces = out;
4385         loadmodel->num_surfaces = count;
4386
4387         i = 0;
4388         for (meshnum = 0;i < count;meshnum++)
4389         {
4390                 oldi = i;
4391                 oldin = in;
4392                 oldout = out;
4393                 meshvertices = 0;
4394                 meshtriangles = 0;
4395                 for (;i < count;i++, in++, out++)
4396                 {
4397                         // check face type first
4398                         type = LittleLong(in->type);
4399                         if (type != Q3FACETYPE_POLYGON
4400                          && type != Q3FACETYPE_PATCH
4401                          && type != Q3FACETYPE_MESH
4402                          && type != Q3FACETYPE_FLARE)
4403                         {
4404                                 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, type);
4405                                 continue;
4406                         }
4407
4408                         n = LittleLong(in->textureindex);
4409                         if (n < 0 || n >= loadmodel->num_textures)
4410                         {
4411                                 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->num_textures);
4412                                 continue;
4413                         }
4414                         out->texture = loadmodel->data_textures + n;
4415                         n = LittleLong(in->effectindex);
4416                         if (n < -1 || n >= loadmodel->brushq3.num_effects)
4417                         {
4418                                 if (developer.integer >= 2)
4419                                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
4420                                 n = -1;
4421                         }
4422                         if (n == -1)
4423                                 out->effect = NULL;
4424                         else
4425                                 out->effect = loadmodel->brushq3.data_effects + n;
4426                         n = LittleLong(in->lightmapindex);
4427                         if (n >= loadmodel->brushq3.num_lightmaps)
4428                         {
4429                                 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
4430                                 n = -1;
4431                         }
4432                         else if (n < 0)
4433                                 n = -1;
4434                         if (n == -1)
4435                                 out->lightmaptexture = NULL;
4436                         else
4437                                 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
4438
4439                         firstvertex = LittleLong(in->firstvertex);
4440                         numvertices = LittleLong(in->numvertices);
4441                         firstelement = LittleLong(in->firstelement);
4442                         numtriangles = LittleLong(in->numelements) / 3;
4443                         if (numtriangles * 3 != LittleLong(in->numelements))
4444                         {
4445                                 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));
4446                                 continue;
4447                         }
4448                         if (firstvertex < 0 || firstvertex + numvertices > loadmodel->brushq3.num_vertices)
4449                         {
4450                                 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);
4451                                 continue;
4452                         }
4453                         if (firstelement < 0 || firstelement + numtriangles * 3 > loadmodel->brushq3.num_triangles * 3)
4454                         {
4455                                 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);
4456                                 continue;
4457                         }
4458                         switch(type)
4459                         {
4460                         case Q3FACETYPE_POLYGON:
4461                         case Q3FACETYPE_MESH:
4462                                 // no processing necessary
4463                                 break;
4464                         case Q3FACETYPE_PATCH:
4465                                 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
4466                                 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
4467                                 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))
4468                                 {
4469                                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
4470                                         continue;
4471                                 }
4472                                 originalvertex3f = loadmodel->brushq3.data_vertex3f + firstvertex * 3;
4473                                 // convert patch to Q3FACETYPE_MESH
4474                                 xtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
4475                                 ytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
4476                                 // bound to user settings
4477                                 xtess = bound(r_subdivisions_mintess.integer, xtess, r_subdivisions_maxtess.integer);
4478                                 ytess = bound(r_subdivisions_mintess.integer, ytess, r_subdivisions_maxtess.integer);
4479                                 // bound to sanity settings
4480                                 xtess = bound(1, xtess, 1024);
4481                                 ytess = bound(1, ytess, 1024);
4482                                 // bound to user limit on vertices
4483                                 while ((xtess > 1 || ytess > 1) && (((patchsize[0] - 1) * xtess) + 1) * (((patchsize[1] - 1) * ytess) + 1) > min(r_subdivisions_maxvertices.integer, 262144))
4484                                 {
4485                                         if (xtess > ytess)
4486                                                 xtess--;
4487                                         else
4488                                                 ytess--;
4489                                 }
4490                                 finalwidth = ((patchsize[0] - 1) * xtess) + 1;
4491                                 finalheight = ((patchsize[1] - 1) * ytess) + 1;
4492                                 numvertices = finalwidth * finalheight;
4493                                 numtriangles = (finalwidth - 1) * (finalheight - 1) * 2;
4494                                 break;
4495                         case Q3FACETYPE_FLARE:
4496                                 if (developer.integer >= 2)
4497                                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
4498                                 // don't render it
4499                                 continue;
4500                         }
4501                         out->num_vertices = numvertices;
4502                         out->num_triangles = numtriangles;
4503                         if (meshvertices + out->num_vertices > 65536)
4504                                 break;
4505                         meshvertices += out->num_vertices;
4506                         meshtriangles += out->num_triangles;
4507                 }
4508
4509                 i = oldi;
4510                 in = oldin;
4511                 out = oldout;
4512                 mesh = tempmeshlist[meshnum] = Mod_AllocSurfMesh(loadmodel->mempool, meshvertices, meshtriangles, false, true, false);
4513                 meshvertices = 0;
4514                 meshtriangles = 0;
4515                 for (;i < count && meshvertices + out->num_vertices <= mesh->num_vertices;i++, in++, out++)
4516                 {
4517                         if (out->num_vertices < 3 || out->num_triangles < 1)
4518                                 continue;
4519
4520                         type = LittleLong(in->type);
4521                         firstvertex = LittleLong(in->firstvertex);
4522                         firstelement = LittleLong(in->firstelement);
4523                         out->groupmesh = mesh;
4524                         out->num_firstvertex = meshvertices;
4525                         out->num_firsttriangle = meshtriangles;
4526                         switch(type)
4527                         {
4528                         case Q3FACETYPE_POLYGON:
4529                         case Q3FACETYPE_MESH:
4530                                 // no processing necessary
4531                                 for (j = 0;j < out->num_vertices;j++)
4532                                 {
4533                                         (out->groupmesh->data_vertex3f + 3 * out->num_firstvertex)[j * 3 + 0] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 0];
4534                                         (out->groupmesh->data_vertex3f + 3 * out->num_firstvertex)[j * 3 + 1] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 1];
4535                                         (out->groupmesh->data_vertex3f + 3 * out->num_firstvertex)[j * 3 + 2] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 2];
4536                                         (out->groupmesh->data_texcoordtexture2f + 2 * out->num_firstvertex)[j * 2 + 0] = loadmodel->brushq3.data_texcoordtexture2f[(firstvertex + j) * 2 + 0];
4537                                         (out->groupmesh->data_texcoordtexture2f + 2 * out->num_firstvertex)[j * 2 + 1] = loadmodel->brushq3.data_texcoordtexture2f[(firstvertex + j) * 2 + 1];
4538                                         (out->groupmesh->data_texcoordlightmap2f + 2 * out->num_firstvertex)[j * 2 + 0] = loadmodel->brushq3.data_texcoordlightmap2f[(firstvertex + j) * 2 + 0];
4539                                         (out->groupmesh->data_texcoordlightmap2f + 2 * out->num_firstvertex)[j * 2 + 1] = loadmodel->brushq3.data_texcoordlightmap2f[(firstvertex + j) * 2 + 1];
4540                                         (out->groupmesh->data_lightmapcolor4f + 4 * out->num_firstvertex)[j * 4 + 0] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 0];
4541                                         (out->groupmesh->data_lightmapcolor4f + 4 * out->num_firstvertex)[j * 4 + 1] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 1];
4542                                         (out->groupmesh->data_lightmapcolor4f + 4 * out->num_firstvertex)[j * 4 + 2] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 2];
4543                                         (out->groupmesh->data_lightmapcolor4f + 4 * out->num_firstvertex)[j * 4 + 3] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 3];
4544                                 }
4545                                 for (j = 0;j < out->num_triangles*3;j++)
4546                                         (out->groupmesh->data_element3i + 3 * out->num_firsttriangle)[j] = loadmodel->brushq3.data_element3i[firstelement + j] + out->num_firstvertex;
4547                                 break;
4548                         case Q3FACETYPE_PATCH:
4549                                 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
4550                                 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
4551                                 originalvertex3f = loadmodel->brushq3.data_vertex3f + firstvertex * 3;
4552                                 originaltexcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + firstvertex * 2;
4553                                 originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + firstvertex * 2;
4554                                 originalcolor4f = loadmodel->brushq3.data_color4f + firstvertex * 4;
4555                                 // convert patch to Q3FACETYPE_MESH
4556                                 xtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
4557                                 ytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
4558                                 // bound to user settings
4559                                 xtess = bound(r_subdivisions_mintess.integer, xtess, r_subdivisions_maxtess.integer);
4560                                 ytess = bound(r_subdivisions_mintess.integer, ytess, r_subdivisions_maxtess.integer);
4561                                 // bound to sanity settings
4562                                 xtess = bound(1, xtess, 1024);
4563                                 ytess = bound(1, ytess, 1024);
4564                                 // bound to user limit on vertices
4565                                 while ((xtess > 1 || ytess > 1) && (((patchsize[0] - 1) * xtess) + 1) * (((patchsize[1] - 1) * ytess) + 1) > min(r_subdivisions_maxvertices.integer, 262144))
4566                                 {
4567                                         if (xtess > ytess)
4568                                                 xtess--;
4569                                         else
4570                                                 ytess--;
4571                                 }
4572                                 finalwidth = ((patchsize[0] - 1) * xtess) + 1;
4573                                 finalheight = ((patchsize[1] - 1) * ytess) + 1;
4574                                 finalvertices = finalwidth * finalheight;
4575                                 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
4576                                 type = Q3FACETYPE_MESH;
4577                                 // generate geometry
4578                                 // (note: normals are skipped because they get recalculated)
4579                                 Q3PatchTesselateFloat(3, sizeof(float[3]), (out->groupmesh->data_vertex3f + 3 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[3]), originalvertex3f, xtess, ytess);
4580                                 Q3PatchTesselateFloat(2, sizeof(float[2]), (out->groupmesh->data_texcoordtexture2f + 2 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[2]), originaltexcoordtexture2f, xtess, ytess);
4581                                 Q3PatchTesselateFloat(2, sizeof(float[2]), (out->groupmesh->data_texcoordlightmap2f + 2 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[2]), originaltexcoordlightmap2f, xtess, ytess);
4582                                 Q3PatchTesselateFloat(4, sizeof(float[4]), (out->groupmesh->data_lightmapcolor4f + 4 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[4]), originalcolor4f, xtess, ytess);
4583                                 Q3PatchTriangleElements((out->groupmesh->data_element3i + 3 * out->num_firsttriangle), finalwidth, finalheight, out->num_firstvertex);
4584                                 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);
4585                                 if (developer.integer >= 2)
4586                                 {
4587                                         if (out->num_triangles < finaltriangles)
4588                                                 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);
4589                                         else
4590                                                 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);
4591                                 }
4592                                 // q3map does not put in collision brushes for curves... ugh
4593                                 // build the lower quality collision geometry
4594                                 xtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value);
4595                                 ytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value);
4596                                 // bound to user settings
4597                                 xtess = bound(r_subdivisions_collision_mintess.integer, xtess, r_subdivisions_collision_maxtess.integer);
4598                                 ytess = bound(r_subdivisions_collision_mintess.integer, ytess, r_subdivisions_collision_maxtess.integer);
4599                                 // bound to sanity settings
4600                                 xtess = bound(1, xtess, 1024);
4601                                 ytess = bound(1, ytess, 1024);
4602                                 // bound to user limit on vertices
4603                                 while ((xtess > 1 || ytess > 1) && (((patchsize[0] - 1) * xtess) + 1) * (((patchsize[1] - 1) * ytess) + 1) > min(r_subdivisions_collision_maxvertices.integer, 262144))
4604                                 {
4605                                         if (xtess > ytess)
4606                                                 xtess--;
4607                                         else
4608                                                 ytess--;
4609                                 }
4610                                 finalwidth = ((patchsize[0] - 1) * xtess) + 1;
4611                                 finalheight = ((patchsize[1] - 1) * ytess) + 1;
4612                                 finalvertices = finalwidth * finalheight;
4613                                 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
4614
4615                                 out->data_collisionvertex3f = (float *)Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * finalvertices);
4616                                 out->data_collisionelement3i = (int *)Mem_Alloc(loadmodel->mempool, sizeof(int[3]) * finaltriangles);
4617                                 out->num_collisionvertices = finalvertices;
4618                                 out->num_collisiontriangles = finaltriangles;
4619                                 Q3PatchTesselateFloat(3, sizeof(float[3]), out->data_collisionvertex3f, patchsize[0], patchsize[1], sizeof(float[3]), originalvertex3f, xtess, ytess);
4620                                 Q3PatchTriangleElements(out->data_collisionelement3i, finalwidth, finalheight, 0);
4621
4622                                 //Mod_SnapVertices(3, out->num_vertices, (out->groupmesh->data_vertex3f + 3 * out->num_firstvertex), 0.25);
4623                                 Mod_SnapVertices(3, out->num_collisionvertices, out->data_collisionvertex3f, 1);
4624
4625                                 oldnumtriangles = out->num_triangles;
4626                                 oldnumtriangles2 = out->num_collisiontriangles;
4627                                 out->num_collisiontriangles = Mod_RemoveDegenerateTriangles(out->num_collisiontriangles, out->data_collisionelement3i, out->data_collisionelement3i, out->data_collisionvertex3f);
4628                                 if (developer.integer)
4629                                         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);
4630                                 break;
4631                         default:
4632                                 break;
4633                         }
4634                         meshvertices += out->num_vertices;
4635                         meshtriangles += out->num_triangles;
4636                         for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++)
4637                                 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)
4638                                         invalidelements++;
4639                         if (invalidelements)
4640                         {
4641                                 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);
4642                                 for (j = 0;j < out->num_triangles * 3;j++)
4643                                 {
4644                                         Con_Printf(" %i", (out->groupmesh->data_element3i + 3 * out->num_firsttriangle)[j] - out->num_firstvertex);
4645                                         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)
4646                                                 (out->groupmesh->data_element3i + 3 * out->num_firsttriangle)[j] = out->num_firstvertex;
4647                                 }
4648                                 Con_Print("\n");
4649                         }
4650                         // for per pixel lighting
4651                         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);
4652                         // calculate a bounding box
4653                         VectorClear(out->mins);
4654                         VectorClear(out->maxs);
4655                         if (out->num_vertices)
4656                         {
4657                                 VectorCopy((out->groupmesh->data_vertex3f + 3 * out->num_firstvertex), out->mins);
4658                                 VectorCopy((out->groupmesh->data_vertex3f + 3 * out->num_firstvertex), out->maxs);
4659                                 for (j = 1, v = (out->groupmesh->data_vertex3f + 3 * out->num_firstvertex) + 3;j < out->num_vertices;j++, v += 3)
4660                                 {
4661                                         out->mins[0] = min(out->mins[0], v[0]);
4662                                         out->maxs[0] = max(out->maxs[0], v[0]);
4663                                         out->mins[1] = min(out->mins[1], v[1]);
4664                                         out->maxs[1] = max(out->maxs[1], v[1]);
4665                                         out->mins[2] = min(out->mins[2], v[2]);
4666                                         out->maxs[2] = max(out->maxs[2], v[2]);
4667                                 }
4668                                 out->mins[0] -= 1.0f;
4669                                 out->mins[1] -= 1.0f;
4670                                 out->mins[2] -= 1.0f;
4671                                 out->maxs[0] += 1.0f;
4672                                 out->maxs[1] += 1.0f;
4673                                 out->maxs[2] += 1.0f;
4674                         }
4675                         // set lightmap styles for consistency with q1bsp
4676                         //out->lightmapinfo->styles[0] = 0;
4677                         //out->lightmapinfo->styles[1] = 255;
4678                         //out->lightmapinfo->styles[2] = 255;
4679                         //out->lightmapinfo->styles[3] = 255;
4680                 }
4681         }
4682
4683         // now store the completed list of meshes
4684         loadmodel->nummeshes = meshnum;
4685         if (loadmodel->nummeshes)
4686         {
4687                 loadmodel->meshlist = (surfmesh_t **)Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t *) * loadmodel->nummeshes);
4688                 memcpy(loadmodel->meshlist, tempmeshlist, sizeof(surfmesh_t *) * loadmodel->nummeshes);
4689         }
4690
4691         // free the no longer needed vertex data
4692         loadmodel->brushq3.num_vertices = 0;
4693         Mem_Free(loadmodel->brushq3.data_vertex3f);
4694         loadmodel->brushq3.data_vertex3f = NULL;
4695         loadmodel->brushq3.data_texcoordtexture2f = NULL;
4696         loadmodel->brushq3.data_texcoordlightmap2f = NULL;
4697         loadmodel->brushq3.data_color4f = NULL;
4698         // free the no longer needed triangle data
4699         loadmodel->brushq3.num_triangles = 0;
4700         Mem_Free(loadmodel->brushq3.data_element3i);
4701         loadmodel->brushq3.data_element3i = NULL;
4702 }
4703
4704 static void Mod_Q3BSP_LoadModels(lump_t *l)
4705 {
4706         q3dmodel_t *in;
4707         q3dmodel_t *out;
4708         int i, j, n, c, count;
4709
4710         in = (q3dmodel_t *)(mod_base + l->fileofs);
4711         if (l->filelen % sizeof(*in))
4712                 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
4713         count = l->filelen / sizeof(*in);
4714         out = (q3dmodel_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4715
4716         loadmodel->brushq3.data_models = out;
4717         loadmodel->brushq3.num_models = count;
4718
4719         for (i = 0;i < count;i++, in++, out++)
4720         {
4721                 for (j = 0;j < 3;j++)
4722                 {
4723                         out->mins[j] = LittleFloat(in->mins[j]);
4724                         out->maxs[j] = LittleFloat(in->maxs[j]);
4725                 }
4726                 n = LittleLong(in->firstface);
4727                 c = LittleLong(in->numfaces);
4728                 if (n < 0 || n + c > loadmodel->num_surfaces)
4729                         Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)", n, n + c, loadmodel->num_surfaces);
4730                 out->firstface = n;
4731                 out->numfaces = c;
4732                 n = LittleLong(in->firstbrush);
4733                 c = LittleLong(in->numbrushes);
4734                 if (n < 0 || n + c > loadmodel->brush.num_brushes)
4735                         Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)", n, n + c, loadmodel->brush.num_brushes);
4736                 out->firstbrush = n;
4737                 out->numbrushes = c;
4738         }
4739 }
4740
4741 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
4742 {
4743         int *in;
4744         int *out;
4745         int i, n, count;
4746
4747         in = (int *)(mod_base + l->fileofs);
4748         if (l->filelen % sizeof(*in))
4749                 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
4750         count = l->filelen / sizeof(*in);
4751         out = (int *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4752
4753         loadmodel->brush.data_leafbrushes = out;
4754         loadmodel->brush.num_leafbrushes = count;
4755
4756         for (i = 0;i < count;i++, in++, out++)
4757         {
4758                 n = LittleLong(*in);
4759                 if (n < 0 || n >= loadmodel->brush.num_brushes)
4760                         Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)", n, loadmodel->brush.num_brushes);
4761                 *out = n;
4762         }
4763 }
4764
4765 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
4766 {
4767         int *in;
4768         int *out;
4769         int i, n, count;
4770
4771         in = (int *)(mod_base + l->fileofs);
4772         if (l->filelen % sizeof(*in))
4773                 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
4774         count = l->filelen / sizeof(*in);
4775         out = (int *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4776
4777         loadmodel->brush.data_leafsurfaces = out;
4778         loadmodel->brush.num_leafsurfaces = count;
4779
4780         for (i = 0;i < count;i++, in++, out++)
4781         {
4782                 n = LittleLong(*in);
4783                 if (n < 0 || n >= loadmodel->num_surfaces)
4784                         Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)", n, loadmodel->num_surfaces);
4785                 *out = n;
4786         }
4787 }
4788
4789 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
4790 {
4791         q3dleaf_t *in;
4792         mleaf_t *out;
4793         int i, j, n, c, count;
4794
4795         in = (q3dleaf_t *)(mod_base + l->fileofs);
4796         if (l->filelen % sizeof(*in))
4797                 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
4798         count = l->filelen / sizeof(*in);
4799         out = (mleaf_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4800
4801         loadmodel->brush.data_leafs = out;
4802         loadmodel->brush.num_leafs = count;
4803
4804         for (i = 0;i < count;i++, in++, out++)
4805         {
4806                 out->parent = NULL;
4807                 out->plane = NULL;
4808                 out->clusterindex = LittleLong(in->clusterindex);
4809                 out->areaindex = LittleLong(in->areaindex);
4810                 for (j = 0;j < 3;j++)
4811                 {
4812                         // yes the mins/maxs are ints
4813                         out->mins[j] = LittleLong(in->mins[j]) - 1;
4814                         out->maxs[j] = LittleLong(in->maxs[j]) + 1;
4815                 }
4816                 n = LittleLong(in->firstleafface);
4817                 c = LittleLong(in->numleaffaces);
4818                 if (n < 0 || n + c > loadmodel->brush.num_leafsurfaces)
4819                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafsurface range %i : %i (%i leafsurfaces)", n, n + c, loadmodel->brush.num_leafsurfaces);
4820                 out->firstleafsurface = loadmodel->brush.data_leafsurfaces + n;
4821                 out->numleafsurfaces = c;
4822                 n = LittleLong(in->firstleafbrush);
4823                 c = LittleLong(in->numleafbrushes);
4824                 if (n < 0 || n + c > loadmodel->brush.num_leafbrushes)
4825                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)", n, n + c, loadmodel->brush.num_leafbrushes);
4826                 out->firstleafbrush = loadmodel->brush.data_leafbrushes + n;
4827                 out->numleafbrushes = c;
4828         }
4829 }
4830
4831 static void Mod_Q3BSP_LoadNodes(lump_t *l)
4832 {
4833         q3dnode_t *in;
4834         mnode_t *out;
4835         int i, j, n, count;
4836
4837         in = (q3dnode_t *)(mod_base + l->fileofs);
4838         if (l->filelen % sizeof(*in))
4839                 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
4840         count = l->filelen / sizeof(*in);
4841         out = (mnode_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4842
4843         loadmodel->brush.data_nodes = out;
4844         loadmodel->brush.num_nodes = count;
4845
4846         for (i = 0;i < count;i++, in++, out++)
4847         {
4848                 out->parent = NULL;
4849                 n = LittleLong(in->planeindex);
4850                 if (n < 0 || n >= loadmodel->brush.num_planes)
4851                         Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)", n, loadmodel->brush.num_planes);
4852                 out->plane = loadmodel->brush.data_planes + n;
4853                 for (j = 0;j < 2;j++)
4854                 {
4855                         n = LittleLong(in->childrenindex[j]);
4856                         if (n >= 0)
4857                         {
4858                                 if (n >= loadmodel->brush.num_nodes)
4859                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)", n, loadmodel->brush.num_nodes);
4860                                 out->children[j] = loadmodel->brush.data_nodes + n;
4861                         }
4862                         else
4863                         {
4864                                 n = -1 - n;
4865                                 if (n >= loadmodel->brush.num_leafs)
4866                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)", n, loadmodel->brush.num_leafs);
4867                                 out->children[j] = (mnode_t *)(loadmodel->brush.data_leafs + n);
4868                         }
4869                 }
4870                 for (j = 0;j < 3;j++)
4871                 {
4872                         // yes the mins/maxs are ints
4873                         out->mins[j] = LittleLong(in->mins[j]) - 1;
4874                         out->maxs[j] = LittleLong(in->maxs[j]) + 1;
4875                 }
4876         }
4877
4878         // set the parent pointers
4879         Mod_Q1BSP_LoadNodes_RecursiveSetParent(loadmodel->brush.data_nodes, NULL);
4880 }
4881
4882 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
4883 {
4884         q3dlightgrid_t *in;
4885         q3dlightgrid_t *out;
4886         int count;
4887
4888         in = (q3dlightgrid_t *)(mod_base + l->fileofs);
4889         if (l->filelen % sizeof(*in))
4890                 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
4891         loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
4892         loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
4893         loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
4894         loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4895         loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4896         loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4897         loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4898         loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4899         loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4900         loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4901         loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4902         loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4903         count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4904         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]);
4905         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]);
4906
4907         // if lump is empty there is nothing to load, we can deal with that in the LightPoint code
4908         if (l->filelen)
4909         {
4910                 if (l->filelen < count * (int)sizeof(*in))
4911                         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]);
4912                 if (l->filelen != count * (int)sizeof(*in))
4913                         Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i", count * sizeof(*in), l->filelen);
4914                 out = (q3dlightgrid_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4915                 loadmodel->brushq3.data_lightgrid = out;
4916                 loadmodel->brushq3.num_lightgrid = count;
4917                 // no swapping or validation necessary
4918                 memcpy(out, in, count * (int)sizeof(*out));
4919         }
4920 }
4921
4922 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4923 {
4924         q3dpvs_t *in;
4925         int totalchains;
4926
4927         if (l->filelen == 0)
4928         {
4929                 int i;
4930                 // unvised maps often have cluster indices even without pvs, so check
4931                 // leafs to find real number of clusters
4932                 loadmodel->brush.num_pvsclusters = 1;
4933                 for (i = 0;i < loadmodel->brush.num_leafs;i++)
4934                         loadmodel->brush.num_pvsclusters = max(loadmodel->brush.num_pvsclusters, loadmodel->brush.data_leafs[i].clusterindex + 1);
4935
4936                 // create clusters
4937                 loadmodel->brush.num_pvsclusterbytes = (loadmodel->brush.num_pvsclusters + 7) / 8;
4938                 totalchains = loadmodel->brush.num_pvsclusterbytes * loadmodel->brush.num_pvsclusters;
4939                 loadmodel->brush.data_pvsclusters = (unsigned char *)Mem_Alloc(loadmodel->mempool, totalchains);
4940                 memset(loadmodel->brush.data_pvsclusters, 0xFF, totalchains);
4941                 return;
4942         }
4943
4944         in = (q3dpvs_t *)(mod_base + l->fileofs);
4945         if (l->filelen < 9)
4946                 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4947
4948         loadmodel->brush.num_pvsclusters = LittleLong(in->numclusters);
4949         loadmodel->brush.num_pvsclusterbytes = LittleLong(in->chainlength);
4950         if (loadmodel->brush.num_pvsclusterbytes < ((loadmodel->brush.num_pvsclusters + 7) / 8))
4951                 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8", loadmodel->brush.num_pvsclusterbytes, loadmodel->brush.num_pvsclusters);
4952         totalchains = loadmodel->brush.num_pvsclusterbytes * loadmodel->brush.num_pvsclusters;
4953         if (l->filelen < totalchains + (int)sizeof(*in))
4954                 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);
4955
4956         loadmodel->brush.data_pvsclusters = (unsigned char *)Mem_Alloc(loadmodel->mempool, totalchains);
4957         memcpy(loadmodel->brush.data_pvsclusters, (unsigned char *)(in + 1), totalchains);
4958 }
4959
4960 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4961 {
4962         int i, j, k, index[3];
4963         float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4964         q3dlightgrid_t *a, *s;
4965         if (!model->brushq3.num_lightgrid)
4966         {
4967                 ambientcolor[0] = 1;
4968                 ambientcolor[1] = 1;
4969                 ambientcolor[2] = 1;
4970                 return;
4971         }
4972         Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4973         //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4974         //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4975         transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0] - 1);
4976         transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1] - 1);
4977         transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2] - 1);
4978         index[0] = (int)floor(transformed[0]);
4979         index[1] = (int)floor(transformed[1]);
4980         index[2] = (int)floor(transformed[2]);
4981         //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4982         // now lerp the values
4983         VectorClear(diffusenormal);
4984         a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4985         for (k = 0;k < 2;k++)
4986         {
4987                 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4988                 if (blend1 < 0.001f || index[2] + k >= model->brushq3.num_lightgrid_isize[2])
4989                         continue;
4990                 for (j = 0;j < 2;j++)
4991                 {
4992                         blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4993                         if (blend2 < 0.001f || index[1] + j >= model->brushq3.num_lightgrid_isize[1])
4994                                 continue;
4995                         for (i = 0;i < 2;i++)
4996                         {
4997                                 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4998                                 if (blend < 0.001f || index[0] + i >= model->brushq3.num_lightgrid_isize[0])
4999                                         continue;
5000                                 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
5001                                 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
5002                                 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
5003                                 pitch = s->diffusepitch * M_PI / 128;
5004                                 yaw = s->diffuseyaw * M_PI / 128;
5005                                 sinpitch = sin(pitch);
5006                                 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
5007                                 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
5008                                 diffusenormal[2] += blend * (cos(pitch));
5009                                 //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)));
5010                         }
5011                 }
5012         }
5013         VectorNormalize(diffusenormal);
5014         //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]);
5015 }
5016
5017 static void Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace_t *trace, model_t *model, mnode_t *node, const vec3_t point, int markframe)
5018 {
5019         int i;
5020         mleaf_t *leaf;
5021         colbrushf_t *brush;
5022         // find which leaf the point is in
5023         while (node->plane)
5024                 node = node->children[DotProduct(point, node->plane->normal) < node->plane->dist];
5025         // point trace the brushes
5026         leaf = (mleaf_t *)node;
5027         for (i = 0;i < leaf->numleafbrushes;i++)
5028         {
5029                 brush = model->brush.data_brushes[leaf->firstleafbrush[i]].colbrushf;
5030                 if (brush && brush->markframe != markframe && BoxesOverlap(point, point, brush->mins, brush->maxs))
5031                 {
5032                         brush->markframe = markframe;
5033                         Collision_TracePointBrushFloat(trace, point, brush);
5034                 }
5035         }
5036         // can't do point traces on curves (they have no thickness)
5037 }
5038
5039 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)
5040 {
5041         int i, startside, endside;
5042         float dist1, dist2, midfrac, mid[3], nodesegmentmins[3], nodesegmentmaxs[3];
5043         mleaf_t *leaf;
5044         msurface_t *surface;
5045         colbrushf_t *brush;
5046         if (startfrac > trace->realfraction)
5047                 return;
5048         // note: all line fragments past first impact fraction are ignored
5049         if (VectorCompare(start, end))
5050         {
5051                 // find which leaf the point is in
5052                 while (node->plane)
5053                         node = node->children[DotProduct(start, node->plane->normal) < node->plane->dist];
5054         }
5055         else
5056         {
5057                 // find which nodes the line is in and recurse for them
5058                 while (node->plane)
5059                 {
5060                         // recurse down node sides
5061                         dist1 = PlaneDiff(start, node->plane);
5062                         dist2 = PlaneDiff(end, node->plane);
5063                         startside = dist1 < 0;
5064                         endside = dist2 < 0;
5065                         if (startside == endside)
5066                         {
5067                                 // most of the time the line fragment is on one side of the plane
5068                                 node = node->children[startside];
5069                         }
5070                         else
5071                         {
5072                                 // line crosses node plane, split the line
5073                                 midfrac = dist1 / (dist1 - dist2);
5074                                 VectorLerp(start, midfrac, end, mid);
5075                                 // take the near side first
5076                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
5077                                 if (midfrac <= trace->realfraction)
5078                                         Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
5079                                 return;
5080                         }
5081                 }
5082         }
5083         // hit a leaf
5084         nodesegmentmins[0] = min(start[0], end[0]);
5085         nodesegmentmins[1] = min(start[1], end[1]);
5086         nodesegmentmins[2] = min(start[2], end[2]);
5087         nodesegmentmaxs[0] = max(start[0], end[0]);
5088         nodesegmentmaxs[1] = max(start[1], end[1]);
5089         nodesegmentmaxs[2] = max(start[2], end[2]);
5090         // line trace the brushes
5091         leaf = (mleaf_t *)node;
5092         for (i = 0;i < leaf->numleafbrushes;i++)
5093         {
5094                 brush = model->brush.data_brushes[leaf->firstleafbrush[i]].colbrushf;
5095                 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
5096                 {
5097                         brush->markframe = markframe;
5098                         Collision_TraceLineBrushFloat(trace, linestart, lineend, brush, brush);
5099                         if (startfrac > trace->realfraction)
5100                                 return;
5101                 }
5102         }
5103         // can't do point traces on curves (they have no thickness)
5104         if (mod_q3bsp_curves_collisions.integer && !VectorCompare(start, end))
5105         {
5106                 // line trace the curves
5107                 for (i = 0;i < leaf->numleafsurfaces;i++)
5108                 {
5109                         surface = model->data_surfaces + leaf->firstleafsurface[i];
5110                         if (surface->num_collisiontriangles && surface->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, surface->mins, surface->maxs))
5111                         {
5112                                 surface->collisionmarkframe = markframe;
5113                                 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, segmentmins, segmentmaxs);
5114                                 if (startfrac > trace->realfraction)
5115                                         return;
5116                         }
5117                 }
5118         }
5119 }
5120
5121 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)
5122 {
5123         int i;
5124         int sides;
5125         mleaf_t *leaf;
5126         colbrushf_t *brush;
5127         msurface_t *surface;
5128         mplane_t *plane;
5129         float nodesegmentmins[3], nodesegmentmaxs[3];
5130 #if 1
5131         for (;;)
5132         {
5133                 plane = node->plane;
5134                 if (!plane)
5135                         break;
5136                 // axial planes are much more common than non-axial, so an optimized
5137                 // axial case pays off here
5138                 // we must handle sides == 0 because of NANs (crashing is far worse than recursing the entire tree!)
5139                 if (plane->type < 3)
5140                 {
5141                         // this is an axial plane, compare bounding box directly to it and
5142                         // recurse sides accordingly
5143                         // recurse down node sides
5144                         // use an inlined axial BoxOnPlaneSide to slightly reduce overhead
5145                         //sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, plane);
5146                         //sides = ((segmentmaxs[plane->type] >= plane->dist) | ((segmentmins[plane->type] < plane->dist) << 1));
5147                         sides = ((segmentmaxs[plane->type] >= plane->dist) + ((segmentmins[plane->type] < plane->dist) * 2));
5148                         if (sides == 3)
5149                         {
5150                                 // segment box crosses plane
5151                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5152                                 sides = 2;
5153                         }
5154                 }
5155                 else
5156                 {
5157                         // this is a non-axial plane, entire trace bounding box
5158                         // comparisons against it are likely to be very sloppy, so in if
5159                         // the whole box is split by the plane we then test the start/end
5160                         // boxes against it to be sure
5161                         sides = BoxOnPlaneSide(segmentmins, segmentmaxs, plane);
5162                         if (sides == 3)
5163                         {
5164                                 // segment box crosses plane
5165                                 // now check start and end brush boxes to handle a lot of 'diagonal' cases more efficiently...
5166                                 sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, plane);
5167                                 if (sides == 3)
5168                                 {
5169                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5170                                         sides = 2;
5171                                 }
5172                         }
5173                 }
5174                 if (sides == 0)
5175                         return; // ERROR: NAN bounding box!
5176                 // take whichever side the segment box is on
5177                 //if (sides < 1 || sides > 2)
5178                 //      Sys_Error("Mod_Q3BSP_TraceBrush_RecursiveBSPNode: side > 2\n");
5179                 node = node->children[sides - 1];
5180         }
5181         nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5182         nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5183         nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5184         nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5185         nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5186         nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5187 #elif 1
5188         for (;;)
5189         {
5190                 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5191                 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5192                 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5193                 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5194                 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5195                 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5196                 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
5197                         return;
5198                 if (!node->plane)
5199                         break;
5200                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5201                 node = node->children[1];
5202         }
5203 #elif 0
5204         // FIXME: could be made faster by copying TraceLine code and making it use
5205         // box plane distances...  (variant on the BoxOnPlaneSide code)
5206         for (;;)
5207         {
5208                 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5209                 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5210                 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5211                 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5212                 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5213                 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5214                 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
5215                         return;
5216                 if (!node->plane)
5217                         break;
5218                 if (mod_q3bsp_debugtracebrush.integer == 2)
5219                 {
5220                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5221                         node = node->children[1];
5222                         continue;
5223                 }
5224                 else if (mod_q3bsp_debugtracebrush.integer == 1)
5225                 {
5226                         // recurse down node sides
5227                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5228                         if (sides == 3)
5229                         {
5230                                 // segment box crosses plane
5231                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5232                                 node = node->children[1];
5233                                 continue;
5234                         }
5235                         if (sides == 0)
5236                                 return; // ERROR: NAN bounding box!
5237                         // take whichever side the segment box is on
5238                         node = node->children[sides - 1];
5239                         continue;
5240                 }
5241                 else
5242                 {
5243                         // recurse down node sides
5244                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5245                         if (sides == 3)
5246                         {
5247                                 // segment box crosses plane
5248                                 // now check start and end brush boxes to handle a lot of 'diagonal' cases more efficiently...
5249                                 sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, node->plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, node->plane);
5250                                 if (sides == 3)
5251                                 {
5252                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5253                                         node = node->children[1];
5254                                         continue;
5255                                 }
5256                         }
5257                         if (sides == 0)
5258                                 return; // ERROR: NAN bounding box!
5259                         // take whichever side the segment box is on
5260                         node = node->children[sides - 1];
5261                         continue;
5262                 }
5263                 return;
5264         }
5265 #else
5266         // FIXME: could be made faster by copying TraceLine code and making it use
5267         // box plane distances...  (variant on the BoxOnPlaneSide code)
5268         for (;;)
5269         {
5270                 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5271                 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5272                 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5273                 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5274                 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5275                 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5276                 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
5277                         return;
5278                 if (!node->plane)
5279                         break;
5280                 if (mod_q3bsp_debugtracebrush.integer == 2)
5281                 {
5282                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5283                         node = node->children[1];
5284                 }
5285                 else if (mod_q3bsp_debugtracebrush.integer == 1)
5286                 {
5287                         // recurse down node sides
5288                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5289                         if (sides == 3)
5290                         {
5291                                 // segment box crosses plane
5292                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5293                                 node = node->children[1];
5294                         }
5295                         else
5296                         {
5297                                 if (sides == 0)
5298                                         return; // ERROR: NAN bounding box!
5299                                 // take whichever side the segment box is on
5300                                 node = node->children[sides - 1];
5301                         }
5302                 }
5303                 else
5304                 {
5305                         // recurse down node sides
5306                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5307                         if (sides == 3)
5308                         {
5309                                 // segment box crosses plane
5310                                 // now check start and end brush boxes to handle a lot of 'diagonal' cases more efficiently...
5311                                 sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, node->plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, node->plane);
5312                                 if (sides == 3)
5313                                 {
5314                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5315                                         sides = 2;
5316                                 }
5317                         }
5318                         if (sides == 0)
5319                                 return; // ERROR: NAN bounding box!
5320                         // take whichever side the segment box is on
5321                         node = node->children[sides - 1];
5322                 }
5323         }
5324 #endif
5325         // hit a leaf
5326         leaf = (mleaf_t *)node;
5327         for (i = 0;i < leaf->numleafbrushes;i++)
5328         {
5329                 brush = model->brush.data_brushes[leaf->firstleafbrush[i]].colbrushf;
5330                 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
5331                 {
5332                         brush->markframe = markframe;
5333                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, brush, brush);
5334                 }
5335         }
5336         if (mod_q3bsp_curves_collisions.integer)
5337         {
5338                 for (i = 0;i < leaf->numleafsurfaces;i++)
5339                 {
5340                         surface = model->data_surfaces + leaf->firstleafsurface[i];
5341                         if (surface->num_collisiontriangles && surface->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, surface->mins, surface->maxs))
5342                         {
5343                                 surface->collisionmarkframe = markframe;
5344                                 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, segmentmins, segmentmaxs);
5345                         }
5346                 }
5347         }
5348 }
5349
5350 static void Mod_Q3BSP_TraceBox(model_t *model, int frame, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask)
5351 {
5352         int i;
5353         float segmentmins[3], segmentmaxs[3];
5354         colbrushf_t *thisbrush_start, *thisbrush_end;
5355         matrix4x4_t startmatrix, endmatrix;
5356         static int markframe = 0;
5357         msurface_t *surface;
5358         q3mbrush_t *brush;
5359         memset(trace, 0, sizeof(*trace));
5360         trace->fraction = 1;
5361         trace->realfraction = 1;
5362         trace->hitsupercontentsmask = hitsupercontentsmask;
5363         startmatrix = identitymatrix;
5364         endmatrix = identitymatrix;
5365         segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
5366         segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
5367         segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
5368         segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
5369         segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
5370         segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
5371         if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
5372         {
5373                 if (VectorCompare(boxstartmins, boxendmins))
5374                 {
5375                         // point trace
5376                         if (model->brush.submodel)
5377                         {
5378                                 for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
5379                                         if (brush->colbrushf)
5380                                                 Collision_TracePointBrushFloat(trace, boxstartmins, brush->colbrushf);
5381                         }
5382                         else
5383                                 Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace, model, model->brush.data_nodes, boxstartmins, ++markframe);
5384                 }
5385                 else
5386                 {
5387                         // line trace
5388                         if (model->brush.submodel)
5389                         {
5390                                 for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
5391                                         if (brush->colbrushf)
5392                                                 Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, brush->colbrushf, brush->colbrushf);
5393                                 if (mod_q3bsp_curves_collisions.integer)
5394                                         for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
5395                                                 if (surface->num_collisiontriangles)
5396                                                         Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, segmentmins, segmentmaxs);
5397                         }
5398                         else
5399                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, model->brush.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe, segmentmins, segmentmaxs);
5400                 }
5401         }
5402         else
5403         {
5404                 // box trace, performed as brush trace
5405                 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
5406                 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
5407                 if (model->brush.submodel)
5408                 {
5409                         for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
5410                                 if (brush->colbrushf)
5411                                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, brush->colbrushf, brush->colbrushf);
5412                         if (mod_q3bsp_curves_collisions.integer)
5413                                 for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
5414                                         if (surface->num_collisiontriangles)
5415                                                 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, segmentmins, segmentmaxs);
5416                 }
5417                 else
5418                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, model->brush.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
5419         }
5420 }
5421
5422 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
5423 {
5424         int supercontents = 0;
5425         if (nativecontents & CONTENTSQ3_SOLID)
5426                 supercontents |= SUPERCONTENTS_SOLID;
5427         if (nativecontents & CONTENTSQ3_WATER)
5428                 supercontents |= SUPERCONTENTS_WATER;
5429         if (nativecontents & CONTENTSQ3_SLIME)
5430                 supercontents |= SUPERCONTENTS_SLIME;
5431         if (nativecontents & CONTENTSQ3_LAVA)
5432                 supercontents |= SUPERCONTENTS_LAVA;
5433         if (nativecontents & CONTENTSQ3_BODY)
5434                 supercontents |= SUPERCONTENTS_BODY;
5435         if (nativecontents & CONTENTSQ3_CORPSE)
5436                 supercontents |= SUPERCONTENTS_CORPSE;
5437         if (nativecontents & CONTENTSQ3_NODROP)
5438                 supercontents |= SUPERCONTENTS_NODROP;
5439         if (nativecontents & CONTENTSQ3_PLAYERCLIP)
5440                 supercontents |= SUPERCONTENTS_PLAYERCLIP;
5441         if (nativecontents & CONTENTSQ3_MONSTERCLIP)
5442                 supercontents |= SUPERCONTENTS_MONSTERCLIP;
5443         if (nativecontents & CONTENTSQ3_DONOTENTER)
5444                 supercontents |= SUPERCONTENTS_DONOTENTER;
5445         return supercontents;
5446 }
5447
5448 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
5449 {
5450         int nativecontents = 0;
5451         if (supercontents & SUPERCONTENTS_SOLID)
5452                 nativecontents |= CONTENTSQ3_SOLID;
5453         if (supercontents & SUPERCONTENTS_WATER)
5454                 nativecontents |= CONTENTSQ3_WATER;
5455         if (supercontents & SUPERCONTENTS_SLIME)
5456                 nativecontents |= CONTENTSQ3_SLIME;
5457         if (supercontents & SUPERCONTENTS_LAVA)
5458                 nativecontents |= CONTENTSQ3_LAVA;
5459         if (supercontents & SUPERCONTENTS_BODY)
5460                 nativecontents |= CONTENTSQ3_BODY;
5461         if (supercontents & SUPERCONTENTS_CORPSE)
5462                 nativecontents |= CONTENTSQ3_CORPSE;
5463         if (supercontents & SUPERCONTENTS_NODROP)
5464                 nativecontents |= CONTENTSQ3_NODROP;
5465         if (supercontents & SUPERCONTENTS_PLAYERCLIP)
5466                 nativecontents |= CONTENTSQ3_PLAYERCLIP;
5467         if (supercontents & SUPERCONTENTS_MONSTERCLIP)
5468                 nativecontents |= CONTENTSQ3_MONSTERCLIP;
5469         if (supercontents & SUPERCONTENTS_DONOTENTER)
5470                 nativecontents |= CONTENTSQ3_DONOTENTER;
5471         return nativecontents;
5472 }
5473
5474 void Mod_Q3BSP_RecursiveFindNumLeafs(mnode_t *node)
5475 {
5476         int numleafs;
5477         while (node->plane)
5478         {
5479                 Mod_Q3BSP_RecursiveFindNumLeafs(node->children[0]);
5480                 node = node->children[1];
5481         }
5482         numleafs = ((mleaf_t *)node - loadmodel->brush.data_leafs) + 1;
5483         if (loadmodel->brush.num_leafs < numleafs)
5484                 loadmodel->brush.num_leafs = numleafs;
5485 }
5486
5487 void Mod_Q3BSP_Load(model_t *mod, void *buffer, void *bufferend)
5488 {
5489         int i, j, numshadowmeshtriangles;
5490         q3dheader_t *header;
5491         float corner[3], yawradius, modelradius;
5492         msurface_t *surface;
5493
5494         mod->type = mod_brushq3;
5495         mod->numframes = 2; // although alternate textures are not supported it is annoying to complain about no such frame 1
5496         mod->numskins = 1;
5497
5498         header = (q3dheader_t *)buffer;
5499
5500         i = LittleLong(header->version);
5501         if (i != Q3BSPVERSION)
5502                 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
5503         mod->brush.ishlbsp = false;
5504         mod->brush.ismcbsp = false;
5505         if (loadmodel->isworldmodel)
5506         {
5507                 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
5508                 Cvar_SetValue("mcbsp", mod->brush.ismcbsp);
5509         }
5510
5511         mod->soundfromcenter = true;
5512         mod->TraceBox = Mod_Q3BSP_TraceBox;
5513         mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
5514         mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
5515         mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
5516         mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
5517         mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
5518         mod->brush.BoxTouchingLeafPVS = Mod_Q1BSP_BoxTouchingLeafPVS;
5519         mod->brush.BoxTouchingVisibleLeafs = Mod_Q1BSP_BoxTouchingVisibleLeafs;
5520         mod->brush.FindBoxClusters = Mod_Q1BSP_FindBoxClusters;
5521         mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
5522         mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
5523         mod->brush.PointInLeaf = Mod_Q1BSP_PointInLeaf;
5524         mod->Draw = R_Q1BSP_Draw;
5525         mod->GetLightInfo = R_Q1BSP_GetLightInfo;
5526         mod->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
5527         mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
5528         mod->DrawLight = R_Q1BSP_DrawLight;
5529
5530         mod_base = (unsigned char *)header;
5531
5532         // swap all the lumps
5533         header->ident = LittleLong(header->ident);
5534         header->version = LittleLong(header->version);
5535         for (i = 0;i < Q3HEADER_LUMPS;i++)
5536         {
5537                 header->lumps[i].fileofs = LittleLong(header->lumps[i].fileofs);
5538                 header->lumps[i].filelen = LittleLong(header->lumps[i].filelen);
5539         }
5540
5541         mod->brush.qw_md4sum = 0;
5542         mod->brush.qw_md4sum2 = 0;
5543         for (i = 0;i < Q3HEADER_LUMPS;i++)
5544         {
5545                 if (i == Q3LUMP_ENTITIES)
5546                         continue;
5547                 mod->brush.qw_md4sum ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen);
5548                 if (i == Q3LUMP_PVS || i == Q3LUMP_LEAFS || i == Q3LUMP_NODES)
5549                         continue;
5550                 mod->brush.qw_md4sum2 ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen);
5551         }
5552
5553         Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
5554         Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
5555         Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
5556         Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
5557         Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
5558         Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
5559         Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
5560         Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
5561         Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
5562         Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
5563         Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
5564         Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
5565         Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
5566         Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
5567         Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
5568         Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
5569         Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
5570         loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
5571
5572         // the MakePortals code works fine on the q3bsp data as well
5573         Mod_Q1BSP_MakePortals();
5574
5575         // make a single combined shadow mesh to allow optimized shadow volume creation
5576         numshadowmeshtriangles = 0;
5577         for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
5578         {
5579                 surface->num_firstshadowmeshtriangle = numshadowmeshtriangles;
5580                 numshadowmeshtriangles += surface->num_triangles;
5581         }
5582         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
5583         for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
5584                 if (surface->groupmesh)
5585                         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));
5586         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true);
5587         Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
5588
5589         loadmodel->brush.num_leafs = 0;
5590         Mod_Q3BSP_RecursiveFindNumLeafs(loadmodel->brush.data_nodes);
5591
5592         if (loadmodel->isworldmodel)
5593         {
5594                 // clear out any stale submodels or worldmodels lying around
5595                 // if we did this clear before now, an error might abort loading and
5596                 // leave things in a bad state
5597                 Mod_RemoveStaleWorldModels(loadmodel);
5598         }
5599
5600         mod = loadmodel;
5601         for (i = 0;i < loadmodel->brush.numsubmodels;i++)
5602         {
5603                 if (i > 0)
5604                 {
5605                         char name[10];
5606                         // LordHavoc: only register submodels if it is the world
5607                         // (prevents external bsp models from replacing world submodels with
5608                         //  their own)
5609                         if (!loadmodel->isworldmodel)
5610                                 continue;
5611                         // duplicate the basic information
5612                         sprintf(name, "*%i", i);
5613                         mod = Mod_FindName(name);
5614                         *mod = *loadmodel;
5615                         strcpy(mod->name, name);
5616                         // textures and memory belong to the main model
5617                         mod->texturepool = NULL;
5618                         mod->mempool = NULL;
5619                         mod->brush.GetPVS = NULL;
5620                         mod->brush.FatPVS = NULL;
5621                         mod->brush.BoxTouchingPVS = NULL;
5622                         mod->brush.BoxTouchingLeafPVS = NULL;
5623                         mod->brush.BoxTouchingVisibleLeafs = NULL;
5624                         mod->brush.FindBoxClusters = NULL;
5625                         mod->brush.LightPoint = NULL;
5626                         mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
5627                 }
5628                 mod->brush.submodel = i;
5629
5630                 // make the model surface list (used by shadowing/lighting)
5631                 mod->firstmodelsurface = mod->brushq3.data_models[i].firstface;
5632                 mod->nummodelsurfaces = mod->brushq3.data_models[i].numfaces;
5633                 mod->firstmodelbrush = mod->brushq3.data_models[i].firstbrush;
5634                 mod->nummodelbrushes = mod->brushq3.data_models[i].numbrushes;
5635                 mod->surfacelist = (int *)Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->surfacelist));
5636                 for (j = 0;j < mod->nummodelsurfaces;j++)
5637                         mod->surfacelist[j] = mod->firstmodelsurface + j;
5638
5639                 VectorCopy(mod->brushq3.data_models[i].mins, mod->normalmins);
5640                 VectorCopy(mod->brushq3.data_models[i].maxs, mod->normalmaxs);
5641                 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
5642                 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
5643                 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
5644                 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
5645                 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
5646                 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
5647                 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
5648                 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
5649                 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
5650                 mod->yawmins[2] = mod->normalmins[2];
5651                 mod->yawmaxs[2] = mod->normalmaxs[2];
5652                 mod->radius = modelradius;
5653                 mod->radius2 = modelradius * modelradius;
5654
5655                 for (j = 0;j < mod->nummodelsurfaces;j++)
5656                         if (mod->data_surfaces[j + mod->firstmodelsurface].texture->surfaceflags & Q3SURFACEFLAG_SKY)
5657                                 break;
5658                 if (j < mod->nummodelsurfaces)
5659                         mod->DrawSky = R_Q1BSP_DrawSky;
5660         }
5661 }
5662
5663 void Mod_IBSP_Load(model_t *mod, void *buffer, void *bufferend)
5664 {
5665         int i = LittleLong(((int *)buffer)[1]);
5666         if (i == Q3BSPVERSION)
5667                 Mod_Q3BSP_Load(mod,buffer, bufferend);
5668         else if (i == Q2BSPVERSION)
5669                 Mod_Q2BSP_Load(mod,buffer, bufferend);
5670         else
5671                 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i", i);
5672 }
5673
5674 void Mod_MAP_Load(model_t *mod, void *buffer, void *bufferend)
5675 {
5676         Host_Error("Mod_MAP_Load: not yet implemented");
5677 }
5678