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