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