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