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