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