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