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