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