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