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