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