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