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