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