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