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