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