]> icculus.org git repositories - divverent/darkplaces.git/blob - model_brush.c
div0 fixed the bug that caused huge console prints to the terminal to be truncated...
[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                         Host_Error("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);
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);
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_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
3160         Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
3161         Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
3162         Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
3163         Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
3164         Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
3165         Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
3166         Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
3167         Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
3168         Mod_Q1BSP_LoadLeaffaces(&header->lumps[LUMP_MARKSURFACES]);
3169         Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
3170         // load submodels before leafs because they contain the number of vis leafs
3171         Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS], &hullinfo);
3172         Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
3173         Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
3174         Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES], &hullinfo);
3175
3176         if (!mod->brushq1.lightdata)
3177                 mod->brush.LightPoint = NULL;
3178
3179         if (mod->brushq1.data_compressedpvs)
3180                 Mem_Free(mod->brushq1.data_compressedpvs);
3181         mod->brushq1.data_compressedpvs = NULL;
3182         mod->brushq1.num_compressedpvs = 0;
3183
3184         Mod_Q1BSP_MakeHull0();
3185         Mod_Q1BSP_MakePortals();
3186
3187         mod->numframes = 2;             // regular and alternate animation
3188         mod->numskins = 1;
3189
3190         mainmempool = mod->mempool;
3191
3192         Mod_Q1BSP_LoadLightList();
3193
3194         // make a single combined shadow mesh to allow optimized shadow volume creation
3195         numshadowmeshtriangles = 0;
3196         for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
3197         {
3198                 surface->num_firstshadowmeshtriangle = numshadowmeshtriangles;
3199                 numshadowmeshtriangles += surface->num_triangles;
3200         }
3201         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
3202         for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
3203                 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));
3204         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true);
3205         Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
3206
3207         if (loadmodel->brush.numsubmodels)
3208                 loadmodel->brush.submodels = (model_t **)Mem_Alloc(loadmodel->mempool, loadmodel->brush.numsubmodels * sizeof(model_t *));
3209
3210         if (loadmodel->isworldmodel)
3211         {
3212                 // clear out any stale submodels or worldmodels lying around
3213                 // if we did this clear before now, an error might abort loading and
3214                 // leave things in a bad state
3215                 Mod_RemoveStaleWorldModels(loadmodel);
3216         }
3217
3218         // LordHavoc: to clear the fog around the original quake submodel code, I
3219         // will explain:
3220         // first of all, some background info on the submodels:
3221         // model 0 is the map model (the world, named maps/e1m1.bsp for example)
3222         // model 1 and higher are submodels (doors and the like, named *1, *2, etc)
3223         // now the weird for loop itself:
3224         // the loop functions in an odd way, on each iteration it sets up the
3225         // current 'mod' model (which despite the confusing code IS the model of
3226         // the number i), at the end of the loop it duplicates the model to become
3227         // the next submodel, and loops back to set up the new submodel.
3228
3229         // LordHavoc: now the explanation of my sane way (which works identically):
3230         // set up the world model, then on each submodel copy from the world model
3231         // and set up the submodel with the respective model info.
3232         for (i = 0;i < mod->brush.numsubmodels;i++)
3233         {
3234                 // LordHavoc: this code was originally at the end of this loop, but
3235                 // has been transformed to something more readable at the start here.
3236
3237                 if (i > 0)
3238                 {
3239                         char name[10];
3240                         // LordHavoc: only register submodels if it is the world
3241                         // (prevents external bsp models from replacing world submodels with
3242                         //  their own)
3243                         if (!loadmodel->isworldmodel)
3244                                 continue;
3245                         // duplicate the basic information
3246                         sprintf(name, "*%i", i);
3247                         mod = Mod_FindName(name);
3248                         // copy the base model to this one
3249                         *mod = *loadmodel;
3250                         // rename the clone back to its proper name
3251                         strcpy(mod->name, name);
3252                         // textures and memory belong to the main model
3253                         mod->texturepool = NULL;
3254                         mod->mempool = NULL;
3255                 }
3256
3257                 mod->brush.submodel = i;
3258
3259                 if (loadmodel->brush.submodels)
3260                         loadmodel->brush.submodels[i] = mod;
3261
3262                 bm = &mod->brushq1.submodels[i];
3263
3264                 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
3265                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
3266                 {
3267                         mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
3268                         mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
3269                 }
3270
3271                 mod->firstmodelsurface = bm->firstface;
3272                 mod->nummodelsurfaces = bm->numfaces;
3273
3274                 // make the model surface list (used by shadowing/lighting)
3275                 mod->surfacelist = (int *)Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->surfacelist));
3276                 for (j = 0;j < mod->nummodelsurfaces;j++)
3277                         mod->surfacelist[j] = mod->firstmodelsurface + j;
3278
3279                 // this gets altered below if sky is used
3280                 mod->DrawSky = NULL;
3281                 mod->Draw = R_Q1BSP_Draw;
3282                 mod->GetLightInfo = R_Q1BSP_GetLightInfo;
3283                 mod->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
3284                 mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
3285                 mod->DrawLight = R_Q1BSP_DrawLight;
3286                 if (i != 0)
3287                 {
3288                         mod->brush.GetPVS = NULL;
3289                         mod->brush.FatPVS = NULL;
3290                         mod->brush.BoxTouchingPVS = NULL;
3291                         mod->brush.BoxTouchingLeafPVS = NULL;
3292                         mod->brush.BoxTouchingVisibleLeafs = NULL;
3293                         mod->brush.FindBoxClusters = NULL;
3294                         mod->brush.LightPoint = NULL;
3295                         mod->brush.AmbientSoundLevelsForPoint = NULL;
3296                 }
3297                 Mod_Q1BSP_BuildLightmapUpdateChains(loadmodel->mempool, mod);
3298                 if (mod->nummodelsurfaces)
3299                 {
3300                         // LordHavoc: calculate bmodel bounding box rather than trusting what it says
3301                         mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
3302                         mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
3303                         modelyawradius = 0;
3304                         modelradius = 0;
3305                         for (j = 0, surface = &mod->data_surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surface++)
3306                         {
3307                                 // we only need to have a drawsky function if it is used(usually only on world model)
3308                                 if (surface->texture->basematerialflags & MATERIALFLAG_SKY)
3309                                         mod->DrawSky = R_Q1BSP_DrawSky;
3310                                 // calculate bounding shapes
3311                                 for (k = 0, vec = (surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex);k < surface->num_vertices;k++, vec += 3)
3312                                 {
3313                                         if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
3314                                         if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
3315                                         if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
3316                                         if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
3317                                         if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
3318                                         if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
3319                                         dist = vec[0]*vec[0]+vec[1]*vec[1];
3320                                         if (modelyawradius < dist)
3321                                                 modelyawradius = dist;
3322                                         dist += vec[2]*vec[2];
3323                                         if (modelradius < dist)
3324                                                 modelradius = dist;
3325                                 }
3326                         }
3327                         modelyawradius = sqrt(modelyawradius);
3328                         modelradius = sqrt(modelradius);
3329                         mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
3330                         mod->yawmins[2] = mod->normalmins[2];
3331                         mod->yawmaxs[2] = mod->normalmaxs[2];
3332                         mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
3333                         mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
3334                         mod->radius = modelradius;
3335                         mod->radius2 = modelradius * modelradius;
3336                 }
3337                 else
3338                 {
3339                         // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
3340                         Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadmodel->name);
3341                 }
3342                 //mod->brushq1.num_visleafs = bm->visleafs;
3343         }
3344
3345         Mod_Q1BSP_LoadMapBrushes();
3346
3347         //Mod_Q1BSP_ProcessLightList();
3348
3349         if (developer.integer)
3350                 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);
3351 }
3352
3353 static void Mod_Q2BSP_LoadEntities(lump_t *l)
3354 {
3355 }
3356
3357 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
3358 {
3359 /*
3360         d_t *in;
3361         m_t *out;
3362         int i, count;
3363
3364         in = (void *)(mod_base + l->fileofs);
3365         if (l->filelen % sizeof(*in))
3366                 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3367         count = l->filelen / sizeof(*in);
3368         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3369
3370         loadmodel-> = out;
3371         loadmodel->num = count;
3372
3373         for (i = 0;i < count;i++, in++, out++)
3374         {
3375         }
3376 */
3377 }
3378
3379 static void Mod_Q2BSP_LoadVertices(lump_t *l)
3380 {
3381 /*
3382         d_t *in;
3383         m_t *out;
3384         int i, count;
3385
3386         in = (void *)(mod_base + l->fileofs);
3387         if (l->filelen % sizeof(*in))
3388                 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3389         count = l->filelen / sizeof(*in);
3390         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3391
3392         loadmodel-> = out;
3393         loadmodel->num = count;
3394
3395         for (i = 0;i < count;i++, in++, out++)
3396         {
3397         }
3398 */
3399 }
3400
3401 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
3402 {
3403 /*
3404         d_t *in;
3405         m_t *out;
3406         int i, count;
3407
3408         in = (void *)(mod_base + l->fileofs);
3409         if (l->filelen % sizeof(*in))
3410                 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
3411         count = l->filelen / sizeof(*in);
3412         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3413
3414         loadmodel-> = out;
3415         loadmodel->num = count;
3416
3417         for (i = 0;i < count;i++, in++, out++)
3418         {
3419         }
3420 */
3421 }
3422
3423 static void Mod_Q2BSP_LoadNodes(lump_t *l)
3424 {
3425 /*
3426         d_t *in;
3427         m_t *out;
3428         int i, count;
3429
3430         in = (void *)(mod_base + l->fileofs);
3431         if (l->filelen % sizeof(*in))
3432                 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3433         count = l->filelen / sizeof(*in);
3434         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3435
3436         loadmodel-> = out;
3437         loadmodel->num = count;
3438
3439         for (i = 0;i < count;i++, in++, out++)
3440         {
3441         }
3442 */
3443 }
3444
3445 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
3446 {
3447 /*
3448         d_t *in;
3449         m_t *out;
3450         int i, count;
3451
3452         in = (void *)(mod_base + l->fileofs);
3453         if (l->filelen % sizeof(*in))
3454                 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
3455         count = l->filelen / sizeof(*in);
3456         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3457
3458         loadmodel-> = out;
3459         loadmodel->num = count;
3460
3461         for (i = 0;i < count;i++, in++, out++)
3462         {
3463         }
3464 */
3465 }
3466
3467 static void Mod_Q2BSP_LoadFaces(lump_t *l)
3468 {
3469 /*
3470         d_t *in;
3471         m_t *out;
3472         int i, count;
3473
3474         in = (void *)(mod_base + l->fileofs);
3475         if (l->filelen % sizeof(*in))
3476                 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3477         count = l->filelen / sizeof(*in);
3478         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3479
3480         loadmodel-> = out;
3481         loadmodel->num = count;
3482
3483         for (i = 0;i < count;i++, in++, out++)
3484         {
3485         }
3486 */
3487 }
3488
3489 static void Mod_Q2BSP_LoadLighting(lump_t *l)
3490 {
3491 /*
3492         d_t *in;
3493         m_t *out;
3494         int i, count;
3495
3496         in = (void *)(mod_base + l->fileofs);
3497         if (l->filelen % sizeof(*in))
3498                 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
3499         count = l->filelen / sizeof(*in);
3500         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3501
3502         loadmodel-> = out;
3503         loadmodel->num = count;
3504
3505         for (i = 0;i < count;i++, in++, out++)
3506         {
3507         }
3508 */
3509 }
3510
3511 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
3512 {
3513 /*
3514         d_t *in;
3515         m_t *out;
3516         int i, count;
3517
3518         in = (void *)(mod_base + l->fileofs);
3519         if (l->filelen % sizeof(*in))
3520                 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3521         count = l->filelen / sizeof(*in);
3522         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3523
3524         loadmodel-> = out;
3525         loadmodel->num = count;
3526
3527         for (i = 0;i < count;i++, in++, out++)
3528         {
3529         }
3530 */
3531 }
3532
3533 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3534 {
3535 /*
3536         d_t *in;
3537         m_t *out;
3538         int i, count;
3539
3540         in = (void *)(mod_base + l->fileofs);
3541         if (l->filelen % sizeof(*in))
3542                 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3543         count = l->filelen / sizeof(*in);
3544         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3545
3546         loadmodel-> = out;
3547         loadmodel->num = count;
3548
3549         for (i = 0;i < count;i++, in++, out++)
3550         {
3551         }
3552 */
3553 }
3554
3555 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3556 {
3557 /*
3558         d_t *in;
3559         m_t *out;
3560         int i, count;
3561
3562         in = (void *)(mod_base + l->fileofs);
3563         if (l->filelen % sizeof(*in))
3564                 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3565         count = l->filelen / sizeof(*in);
3566         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3567
3568         loadmodel-> = out;
3569         loadmodel->num = count;
3570
3571         for (i = 0;i < count;i++, in++, out++)
3572         {
3573         }
3574 */
3575 }
3576
3577 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3578 {
3579 /*
3580         d_t *in;
3581         m_t *out;
3582         int i, count;
3583
3584         in = (void *)(mod_base + l->fileofs);
3585         if (l->filelen % sizeof(*in))
3586                 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3587         count = l->filelen / sizeof(*in);
3588         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3589
3590         loadmodel-> = out;
3591         loadmodel->num = count;
3592
3593         for (i = 0;i < count;i++, in++, out++)
3594         {
3595         }
3596 */
3597 }
3598
3599 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3600 {
3601 /*
3602         d_t *in;
3603         m_t *out;
3604         int i, count;
3605
3606         in = (void *)(mod_base + l->fileofs);
3607         if (l->filelen % sizeof(*in))
3608                 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3609         count = l->filelen / sizeof(*in);
3610         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3611
3612         loadmodel-> = out;
3613         loadmodel->num = count;
3614
3615         for (i = 0;i < count;i++, in++, out++)
3616         {
3617         }
3618 */
3619 }
3620
3621 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3622 {
3623 /*
3624         d_t *in;
3625         m_t *out;
3626         int i, count;
3627
3628         in = (void *)(mod_base + l->fileofs);
3629         if (l->filelen % sizeof(*in))
3630                 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3631         count = l->filelen / sizeof(*in);
3632         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3633
3634         loadmodel-> = out;
3635         loadmodel->num = count;
3636
3637         for (i = 0;i < count;i++, in++, out++)
3638         {
3639         }
3640 */
3641 }
3642
3643 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3644 {
3645 /*
3646         d_t *in;
3647         m_t *out;
3648         int i, count;
3649
3650         in = (void *)(mod_base + l->fileofs);
3651         if (l->filelen % sizeof(*in))
3652                 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3653         count = l->filelen / sizeof(*in);
3654         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3655
3656         loadmodel-> = out;
3657         loadmodel->num = count;
3658
3659         for (i = 0;i < count;i++, in++, out++)
3660         {
3661         }
3662 */
3663 }
3664
3665 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3666 {
3667 /*
3668         d_t *in;
3669         m_t *out;
3670         int i, count;
3671
3672         in = (void *)(mod_base + l->fileofs);
3673         if (l->filelen % sizeof(*in))
3674                 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3675         count = l->filelen / sizeof(*in);
3676         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3677
3678         loadmodel-> = out;
3679         loadmodel->num = count;
3680
3681         for (i = 0;i < count;i++, in++, out++)
3682         {
3683         }
3684 */
3685 }
3686
3687 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3688 {
3689 /*
3690         d_t *in;
3691         m_t *out;
3692         int i, count;
3693
3694         in = (void *)(mod_base + l->fileofs);
3695         if (l->filelen % sizeof(*in))
3696                 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3697         count = l->filelen / sizeof(*in);
3698         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3699
3700         loadmodel-> = out;
3701         loadmodel->num = count;
3702
3703         for (i = 0;i < count;i++, in++, out++)
3704         {
3705         }
3706 */
3707 }
3708
3709 static void Mod_Q2BSP_LoadModels(lump_t *l)
3710 {
3711 /*
3712         d_t *in;
3713         m_t *out;
3714         int i, count;
3715
3716         in = (void *)(mod_base + l->fileofs);
3717         if (l->filelen % sizeof(*in))
3718                 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3719         count = l->filelen / sizeof(*in);
3720         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3721
3722         loadmodel-> = out;
3723         loadmodel->num = count;
3724
3725         for (i = 0;i < count;i++, in++, out++)
3726         {
3727         }
3728 */
3729 }
3730
3731 void static Mod_Q2BSP_Load(model_t *mod, void *buffer, void *bufferend)
3732 {
3733         int i;
3734         q2dheader_t *header;
3735
3736         Host_Error("Mod_Q2BSP_Load: not yet implemented");
3737
3738         mod->type = mod_brushq2;
3739
3740         header = (q2dheader_t *)buffer;
3741
3742         i = LittleLong(header->version);
3743         if (i != Q2BSPVERSION)
3744                 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3745         mod->brush.ishlbsp = false;
3746         mod->brush.ismcbsp = false;
3747         if (loadmodel->isworldmodel)
3748         {
3749                 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3750                 Cvar_SetValue("mcbsp", mod->brush.ismcbsp);
3751         }
3752
3753         mod_base = (unsigned char *)header;
3754
3755         // swap all the lumps
3756         for (i = 0;i < (int) sizeof(*header) / 4;i++)
3757                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3758
3759         // store which lightmap format to use
3760         mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3761
3762         Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3763         Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3764         Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3765         Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3766         Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3767         Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3768         Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3769         Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3770         Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3771         Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3772         Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3773         Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3774         Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3775         Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3776         Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3777         Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3778         Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3779         // LordHavoc: must go last because this makes the submodels
3780         Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3781 }
3782
3783 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents);
3784 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents);
3785
3786 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3787 {
3788         const char *data;
3789         char key[128], value[MAX_INPUTLINE];
3790         float v[3];
3791         loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3792         loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3793         loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3794         if (!l->filelen)
3795                 return;
3796         loadmodel->brush.entities = (char *)Mem_Alloc(loadmodel->mempool, l->filelen);
3797         memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3798         data = loadmodel->brush.entities;
3799         // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3800         if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3801         {
3802                 while (1)
3803                 {
3804                         if (!COM_ParseToken(&data, false))
3805                                 break; // error
3806                         if (com_token[0] == '}')
3807                                 break; // end of worldspawn
3808                         if (com_token[0] == '_')
3809                                 strcpy(key, com_token + 1);
3810                         else
3811                                 strcpy(key, com_token);
3812                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
3813                                 key[strlen(key)-1] = 0;
3814                         if (!COM_ParseToken(&data, false))
3815                                 break; // error
3816                         strcpy(value, com_token);
3817                         if (!strcmp("gridsize", key))
3818                         {
3819                                 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3820                                         VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3821                         }
3822                 }
3823         }
3824 }
3825
3826 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3827 {
3828         q3dtexture_t *in;
3829         texture_t *out;
3830         int i, count;
3831         int j, c;
3832         fssearch_t *search;
3833         char *f;
3834         const char *text;
3835         int flags, flags2, numparameters, passnumber;
3836         char shadername[Q3PATHLENGTH];
3837         char sky[Q3PATHLENGTH];
3838         char firstpasstexturename[Q3PATHLENGTH];
3839         char parameter[4][Q3PATHLENGTH];
3840
3841         in = (q3dtexture_t *)(mod_base + l->fileofs);
3842         if (l->filelen % sizeof(*in))
3843                 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3844         count = l->filelen / sizeof(*in);
3845         out = (texture_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3846
3847         loadmodel->data_textures = out;
3848         loadmodel->num_textures = count;
3849
3850         for (i = 0;i < count;i++, in++, out++)
3851         {
3852                 strlcpy (out->name, in->name, sizeof (out->name));
3853                 out->surfaceflags = LittleLong(in->surfaceflags);
3854                 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, LittleLong(in->contents));
3855                 out->surfaceparms = -1;
3856         }
3857
3858         // do a quick parse of shader files to get surfaceparms
3859         if ((search = FS_Search("scripts/*.shader", true, false)))
3860         {
3861                 for (i = 0;i < search->numfilenames;i++)
3862                 {
3863                         if ((f = (char *)FS_LoadFile(search->filenames[i], tempmempool, false, NULL)))
3864                         {
3865                                 text = f;
3866                                 while (COM_ParseToken(&text, false))
3867                                 {
3868                                         strlcpy (shadername, com_token, sizeof (shadername));
3869                                         flags = 0;
3870                                         flags2 = 0;
3871                                         sky[0] = 0;
3872                                         passnumber = 0;
3873                                         firstpasstexturename[0] = 0;
3874                                         if (COM_ParseToken(&text, false) && !strcasecmp(com_token, "{"))
3875                                         {
3876                                                 while (COM_ParseToken(&text, false))
3877                                                 {
3878                                                         if (!strcasecmp(com_token, "}"))
3879                                                                 break;
3880                                                         else if (!strcasecmp(com_token, "{"))
3881                                                         {
3882                                                                 while (COM_ParseToken(&text, false))
3883                                                                 {
3884                                                                         if (!strcasecmp(com_token, "}"))
3885                                                                                 break;
3886                                                                         if (!strcasecmp(com_token, "\n"))
3887                                                                                 continue;
3888                                                                         numparameters = 0;
3889                                                                         for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
3890                                                                         {
3891                                                                                 if (j < 4)
3892                                                                                 {
3893                                                                                         strlcpy(parameter[j], com_token, sizeof(parameter[j]));
3894                                                                                         numparameters = j + 1;
3895                                                                                 }
3896                                                                                 if (!COM_ParseToken(&text, true))
3897                                                                                         break;
3898                                                                         }
3899                                                                         if (developer.integer >= 2)
3900                                                                         {
3901                                                                                 Con_Printf("%s %i: ", shadername, passnumber);
3902                                                                                 for (j = 0;j < numparameters;j++)
3903                                                                                         Con_Printf(" %s", parameter[j]);
3904                                                                                 Con_Print("\n");
3905                                                                         }
3906                                                                         if (passnumber == 0 && numparameters >= 1)
3907                                                                         {
3908                                                                                 if (!strcasecmp(parameter[0], "blendfunc") && (flags & Q3SURFACEPARM_TRANS))
3909                                                                                 {
3910                                                                                         if (numparameters == 2 && !strcasecmp(parameter[1], "add"))
3911                                                                                                 flags2 |= Q3TEXTUREFLAG_ADDITIVE;
3912                                                                                         else if (numparameters == 3 && !strcasecmp(parameter[1], "gl_one") && !strcasecmp(parameter[2], "gl_one"))
3913                                                                                                 flags2 |= Q3TEXTUREFLAG_ADDITIVE;
3914                                                                                         else if (numparameters == 3 && !strcasecmp(parameter[1], "gl_src_alpha") && !strcasecmp(parameter[2], "gl_one"))
3915                                                                                                 flags2 |= Q3TEXTUREFLAG_ADDITIVE;
3916                                                                                 }
3917                                                                                 else if (numparameters >= 2 && (!strcasecmp(parameter[0], "map") || !strcasecmp(parameter[0], "clampmap")))
3918                                                                                         strlcpy(firstpasstexturename, parameter[1], sizeof(firstpasstexturename));
3919                                                                                 else if (numparameters >= 3 && !strcasecmp(parameter[0], "animmap"))
3920                                                                                         strlcpy(firstpasstexturename, parameter[2], sizeof(firstpasstexturename));
3921                                                                                 else if (numparameters >= 2 && !strcasecmp(parameter[0], "alphafunc"))
3922                                                                                         flags2 |= Q3TEXTUREFLAG_ALPHATEST;
3923                                                                         }
3924                                                                         // break out a level if it was }
3925                                                                         if (!strcasecmp(com_token, "}"))
3926                                                                                 break;
3927                                                                 }
3928                                                                 passnumber++;
3929                                                                 continue;
3930                                                         }
3931                                                         numparameters = 0;
3932                                                         for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
3933                                                         {
3934                                                                 if (j < 4)
3935                                                                 {
3936                                                                         strlcpy(parameter[j], com_token, sizeof(parameter[j]));
3937                                                                         numparameters = j + 1;
3938                                                                 }
3939                                                                 if (!COM_ParseToken(&text, true))
3940                                                                         break;
3941                                                         }
3942                                                         if (i == 0 && !strcasecmp(com_token, "}"))
3943                                                                 break;
3944                                                         if (developer.integer >= 2)
3945                                                         {
3946                                                                 Con_Printf("%s: ", shadername);
3947                                                                 for (j = 0;j < numparameters;j++)
3948                                                                         Con_Printf(" %s", parameter[j]);
3949                                                                 Con_Print("\n");
3950                                                         }
3951                                                         if (numparameters < 1)
3952                                                                 continue;
3953                                                         if (!strcasecmp(parameter[0], "surfaceparm") && numparameters >= 2)
3954                                                         {
3955                                                                 if (!strcasecmp(parameter[1], "alphashadow"))
3956                                                                         flags |= Q3SURFACEPARM_ALPHASHADOW;
3957                                                                 else if (!strcasecmp(parameter[1], "areaportal"))
3958                                                                         flags |= Q3SURFACEPARM_AREAPORTAL;
3959                                                                 else if (!strcasecmp(parameter[1], "clusterportal"))
3960                                                                         flags |= Q3SURFACEPARM_CLUSTERPORTAL;
3961                                                                 else if (!strcasecmp(parameter[1], "detail"))
3962                                                                         flags |= Q3SURFACEPARM_DETAIL;
3963                                                                 else if (!strcasecmp(parameter[1], "donotenter"))
3964                                                                         flags |= Q3SURFACEPARM_DONOTENTER;
3965                                                                 else if (!strcasecmp(parameter[1], "fog"))
3966                                                                         flags |= Q3SURFACEPARM_FOG;
3967                                                                 else if (!strcasecmp(parameter[1], "lava"))
3968                                                                         flags |= Q3SURFACEPARM_LAVA;
3969                                                                 else if (!strcasecmp(parameter[1], "lightfilter"))
3970                                                                         flags |= Q3SURFACEPARM_LIGHTFILTER;
3971                                                                 else if (!strcasecmp(parameter[1], "metalsteps"))
3972                                                                         flags |= Q3SURFACEPARM_METALSTEPS;
3973                                                                 else if (!strcasecmp(parameter[1], "nodamage"))
3974                                                                         flags |= Q3SURFACEPARM_NODAMAGE;
3975                                                                 else if (!strcasecmp(parameter[1], "nodlight"))
3976                                                                         flags |= Q3SURFACEPARM_NODLIGHT;
3977                                                                 else if (!strcasecmp(parameter[1], "nodraw"))
3978                                                                         flags |= Q3SURFACEPARM_NODRAW;
3979                                                                 else if (!strcasecmp(parameter[1], "nodrop"))
3980                                                                         flags |= Q3SURFACEPARM_NODROP;
3981                                                                 else if (!strcasecmp(parameter[1], "noimpact"))
3982                                                                         flags |= Q3SURFACEPARM_NOIMPACT;
3983                                                                 else if (!strcasecmp(parameter[1], "nolightmap"))
3984                                                                         flags |= Q3SURFACEPARM_NOLIGHTMAP;
3985                                                                 else if (!strcasecmp(parameter[1], "nomarks"))
3986                                                                         flags |= Q3SURFACEPARM_NOMARKS;
3987                                                                 else if (!strcasecmp(parameter[1], "nomipmaps"))
3988                                                                         flags |= Q3SURFACEPARM_NOMIPMAPS;
3989                                                                 else if (!strcasecmp(parameter[1], "nonsolid"))
3990                                                                         flags |= Q3SURFACEPARM_NONSOLID;
3991                                                                 else if (!strcasecmp(parameter[1], "origin"))
3992                                                                         flags |= Q3SURFACEPARM_ORIGIN;
3993                                                                 else if (!strcasecmp(parameter[1], "playerclip"))
3994                                                                         flags |= Q3SURFACEPARM_PLAYERCLIP;
3995                                                                 else if (!strcasecmp(parameter[1], "sky"))
3996                                                                         flags |= Q3SURFACEPARM_SKY;
3997                                                                 else if (!strcasecmp(parameter[1], "slick"))
3998                                                                         flags |= Q3SURFACEPARM_SLICK;
3999                                                                 else if (!strcasecmp(parameter[1], "slime"))
4000                                                                         flags |= Q3SURFACEPARM_SLIME;
4001                                                                 else if (!strcasecmp(parameter[1], "structural"))
4002                                                                         flags |= Q3SURFACEPARM_STRUCTURAL;
4003                                                                 else if (!strcasecmp(parameter[1], "trans"))
4004                                                                         flags |= Q3SURFACEPARM_TRANS;
4005                                                                 else if (!strcasecmp(parameter[1], "water"))
4006                                                                         flags |= Q3SURFACEPARM_WATER;
4007                                                                 else if (!strcasecmp(parameter[1], "pointlight"))
4008                                                                         flags |= Q3SURFACEPARM_POINTLIGHT;
4009                                                                 else
4010                                                                         Con_Printf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[i], parameter[1]);
4011                                                         }
4012                                                         else if (!strcasecmp(parameter[0], "sky") && numparameters >= 2)
4013                                                                 strlcpy(sky, parameter[1], sizeof(sky));
4014                                                         else if (!strcasecmp(parameter[0], "skyparms") && numparameters >= 2)
4015                                                         {
4016                                                                 if (!atoi(parameter[1]) && strcasecmp(parameter[1], "-"))
4017                                                                         strlcpy(sky, parameter[1], sizeof(sky));
4018                                                         }
4019                                                         else if (!strcasecmp(parameter[0], "cull") && numparameters >= 2)
4020                                                         {
4021                                                                 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "twosided"))
4022                                                                         flags2 |= Q3TEXTUREFLAG_TWOSIDED;
4023                                                         }
4024                                                         else if (!strcasecmp(parameter[0], "nomipmaps"))
4025                                                                 flags2 |= Q3TEXTUREFLAG_NOMIPMAPS;
4026                                                         else if (!strcasecmp(parameter[0], "nopicmip"))
4027                                                                 flags2 |= Q3TEXTUREFLAG_NOPICMIP;
4028                                                         else if (!strcasecmp(parameter[0], "deformvertexes") && numparameters >= 2)
4029                                                         {
4030                                                                 if (!strcasecmp(parameter[1], "autosprite") && numparameters == 2)
4031                                                                         flags2 |= Q3TEXTUREFLAG_AUTOSPRITE;
4032                                                                 if (!strcasecmp(parameter[1], "autosprite2") && numparameters == 2)
4033                                                                         flags2 |= Q3TEXTUREFLAG_AUTOSPRITE2;
4034                                                         }
4035                                                 }
4036                                                 // add shader to list (shadername and flags)
4037                                                 // actually here we just poke into the texture settings
4038                                                 for (j = 0, out = loadmodel->data_textures;j < loadmodel->num_textures;j++, out++)
4039                                                 {
4040                                                         if (!strcasecmp(out->name, shadername))
4041                                                         {
4042                                                                 out->surfaceparms = flags;
4043                                                                 out->textureflags = flags2;
4044                                                                 out->basematerialflags = 0;
4045                                                                 if (out->surfaceparms & Q3SURFACEPARM_NODRAW)
4046                                                                         out->basematerialflags |= MATERIALFLAG_NODRAW;
4047                                                                 else if (out->surfaceparms & Q3SURFACEPARM_SKY)
4048                                                                         out->basematerialflags |= MATERIALFLAG_SKY;
4049                                                                 else if (out->surfaceparms & Q3SURFACEPARM_LAVA)
4050                                                                         out->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_FULLBRIGHT;
4051                                                                 else if (out->surfaceparms & Q3SURFACEPARM_SLIME)
4052                                                                         out->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_WATERALPHA;
4053                                                                 else if (out->surfaceparms & Q3SURFACEPARM_WATER)
4054                                                                         out->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_WATERALPHA;
4055                                                                 else
4056                                                                         out->basematerialflags |= MATERIALFLAG_WALL;
4057                                                                 if (out->textureflags & Q3TEXTUREFLAG_ALPHATEST)
4058                                                                 {
4059                                                                         // FIXME: support alpha test?
4060                                                                         out->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_TRANSPARENT;
4061                                                                 }
4062                                                                 else if (out->surfaceparms & Q3SURFACEPARM_TRANS)
4063                                                                 {
4064                                                                         if (out->textureflags & Q3TEXTUREFLAG_ADDITIVE)
4065                                                                                 out->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_TRANSPARENT;
4066                                                                         else
4067                                                                                 out->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_TRANSPARENT;
4068                                                                 }
4069                                                                 strlcpy(out->firstpasstexturename, firstpasstexturename, sizeof(out->firstpasstexturename));
4070                                                                 if ((flags & Q3SURFACEPARM_SKY) && sky[0])
4071                                                                 {
4072                                                                         // quake3 seems to append a _ to the skybox name, so this must do so as well
4073                                                                         dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", sky);
4074                                                                 }
4075                                                         }
4076                                                 }
4077                                         }
4078                                         else
4079                                         {
4080                                                 Con_Printf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[i], com_token);
4081                                                 goto parseerror;
4082                                         }
4083                                 }
4084 parseerror:
4085                                 Mem_Free(f);
4086                         }
4087                 }
4088         }
4089
4090         c = 0;
4091         for (j = 0, out = loadmodel->data_textures;j < loadmodel->num_textures;j++, out++)
4092         {
4093                 if (out->surfaceparms == -1)
4094                 {
4095                         c++;
4096                         Con_DPrintf("%s: No shader found for texture \"%s\"\n", loadmodel->name, out->name);
4097                         out->surfaceparms = 0;
4098                         if (out->surfaceflags & Q3SURFACEFLAG_NODRAW)
4099                                 out->basematerialflags |= MATERIALFLAG_NODRAW;
4100                         else if (out->surfaceflags & Q3SURFACEFLAG_SKY)
4101                                 out->basematerialflags |= MATERIALFLAG_SKY;
4102                         else
4103                                 out->basematerialflags |= MATERIALFLAG_WALL;
4104                         // these are defaults
4105                         //if (!strncmp(out->name, "textures/skies/", 15))
4106                         //      out->surfaceparms |= Q3SURFACEPARM_SKY;
4107                         //if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk")
4108                         // || !strcmp(out->name, "nodraw") || !strcmp(out->name, "common/nodraw") || !strcmp(out->name, "textures/common/nodraw"))
4109                         //      out->surfaceparms |= Q3SURFACEPARM_NODRAW;
4110                         //if (R_TextureHasAlpha(out->skin.base))
4111                         //      out->surfaceparms |= Q3SURFACEPARM_TRANS;
4112                 }
4113                 if (cls.state != ca_dedicated)
4114                         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))
4115                                 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))
4116                                         Con_Printf("%s: texture loading for shader \"%s\" failed (first layer \"%s\" not found either)\n", loadmodel->name, out->name, out->firstpasstexturename);
4117                 // no animation
4118                 out->currentframe = out;
4119         }
4120         if (c)
4121                 Con_DPrintf("%s: %i textures missing shaders\n", loadmodel->name, c);
4122 }
4123
4124 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
4125 {
4126         q3dplane_t *in;
4127         mplane_t *out;
4128         int i, count;
4129
4130         in = (q3dplane_t *)(mod_base + l->fileofs);
4131         if (l->filelen % sizeof(*in))
4132                 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
4133         count = l->filelen / sizeof(*in);
4134         out = (mplane_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4135
4136         loadmodel->brush.data_planes = out;
4137         loadmodel->brush.num_planes = count;
4138
4139         for (i = 0;i < count;i++, in++, out++)
4140         {
4141                 out->normal[0] = LittleFloat(in->normal[0]);
4142                 out->normal[1] = LittleFloat(in->normal[1]);
4143                 out->normal[2] = LittleFloat(in->normal[2]);
4144                 out->dist = LittleFloat(in->dist);
4145                 PlaneClassify(out);
4146         }
4147 }
4148
4149 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
4150 {
4151         q3dbrushside_t *in;
4152         q3mbrushside_t *out;
4153         int i, n, count;
4154
4155         in = (q3dbrushside_t *)(mod_base + l->fileofs);
4156         if (l->filelen % sizeof(*in))
4157                 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
4158         count = l->filelen / sizeof(*in);
4159         out = (q3mbrushside_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4160
4161         loadmodel->brush.data_brushsides = out;
4162         loadmodel->brush.num_brushsides = count;
4163
4164         for (i = 0;i < count;i++, in++, out++)
4165         {
4166                 n = LittleLong(in->planeindex);
4167                 if (n < 0 || n >= loadmodel->brush.num_planes)
4168                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)", n, loadmodel->brush.num_planes);
4169                 out->plane = loadmodel->brush.data_planes + n;
4170                 n = LittleLong(in->textureindex);
4171                 if (n < 0 || n >= loadmodel->num_textures)
4172                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)", n, loadmodel->num_textures);
4173                 out->texture = loadmodel->data_textures + n;
4174         }
4175 }
4176
4177 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
4178 {
4179         q3dbrush_t *in;
4180         q3mbrush_t *out;
4181         int i, j, n, c, count, maxplanes;
4182         mplane_t *planes;
4183
4184         in = (q3dbrush_t *)(mod_base + l->fileofs);
4185         if (l->filelen % sizeof(*in))
4186                 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
4187         count = l->filelen / sizeof(*in);
4188         out = (q3mbrush_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4189
4190         loadmodel->brush.data_brushes = out;
4191         loadmodel->brush.num_brushes = count;
4192
4193         maxplanes = 0;
4194         planes = NULL;
4195
4196         for (i = 0;i < count;i++, in++, out++)
4197         {
4198                 n = LittleLong(in->firstbrushside);
4199                 c = LittleLong(in->numbrushsides);
4200                 if (n < 0 || n + c > loadmodel->brush.num_brushsides)
4201                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)", n, n + c, loadmodel->brush.num_brushsides);
4202                 out->firstbrushside = loadmodel->brush.data_brushsides + n;
4203                 out->numbrushsides = c;
4204                 n = LittleLong(in->textureindex);
4205                 if (n < 0 || n >= loadmodel->num_textures)
4206                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)", n, loadmodel->num_textures);
4207                 out->texture = loadmodel->data_textures + n;
4208
4209                 // make a list of mplane_t structs to construct a colbrush from
4210                 if (maxplanes < out->numbrushsides)
4211                 {
4212                         maxplanes = out->numbrushsides;
4213                         if (planes)
4214                                 Mem_Free(planes);
4215                         planes = (mplane_t *)Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
4216                 }
4217                 for (j = 0;j < out->numbrushsides;j++)
4218                 {
4219                         VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
4220                         planes[j].dist = out->firstbrushside[j].plane->dist;
4221                 }
4222                 // make the colbrush from the planes
4223                 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents);
4224         }
4225         if (planes)
4226                 Mem_Free(planes);
4227 }
4228
4229 static void Mod_Q3BSP_LoadEffects(lump_t *l)
4230 {
4231         q3deffect_t *in;
4232         q3deffect_t *out;
4233         int i, n, count;
4234
4235         in = (q3deffect_t *)(mod_base + l->fileofs);
4236         if (l->filelen % sizeof(*in))
4237                 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
4238         count = l->filelen / sizeof(*in);
4239         out = (q3deffect_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4240
4241         loadmodel->brushq3.data_effects = out;
4242         loadmodel->brushq3.num_effects = count;
4243
4244         for (i = 0;i < count;i++, in++, out++)
4245         {
4246                 strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
4247                 n = LittleLong(in->brushindex);
4248                 if (n >= loadmodel->brush.num_brushes)
4249                 {
4250                         Con_Printf("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes), setting to -1\n", n, loadmodel->brush.num_brushes);
4251                         n = -1;
4252                 }
4253                 out->brushindex = n;
4254                 out->unknown = LittleLong(in->unknown);
4255         }
4256 }
4257
4258 static void Mod_Q3BSP_LoadVertices(lump_t *l)
4259 {
4260         q3dvertex_t *in;
4261         int i, count;
4262
4263         in = (q3dvertex_t *)(mod_base + l->fileofs);
4264         if (l->filelen % sizeof(*in))
4265                 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
4266         loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
4267         loadmodel->brushq3.data_vertex3f = (float *)Mem_Alloc(loadmodel->mempool, count * (sizeof(float) * (3 + 2 + 2 + 4)));
4268         loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + count * 3;
4269         loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + count * 2;
4270         loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_texcoordlightmap2f + count * 2;
4271
4272         for (i = 0;i < count;i++, in++)
4273         {
4274                 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
4275                 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
4276                 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
4277                 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
4278                 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
4279                 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
4280                 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
4281                 // svector/tvector are calculated later in face loading
4282                 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
4283                 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
4284                 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
4285                 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
4286         }
4287 }
4288
4289 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
4290 {
4291         int *in;
4292         int *out;
4293         int i, count;
4294
4295         in = (int *)(mod_base + l->fileofs);
4296         if (l->filelen % sizeof(int[3]))
4297                 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
4298         count = l->filelen / sizeof(*in);
4299         out = (int *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4300
4301         loadmodel->brushq3.num_triangles = count / 3;
4302         loadmodel->brushq3.data_element3i = out;
4303
4304         for (i = 0;i < count;i++, in++, out++)
4305         {
4306                 *out = LittleLong(*in);
4307                 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
4308                 {
4309                         Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
4310                         *out = 0;
4311                 }
4312         }
4313 }
4314
4315 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
4316 {
4317         q3dlightmap_t *in;
4318         rtexture_t **out;
4319         int i, count;
4320
4321         if (!l->filelen)
4322                 return;
4323         in = (q3dlightmap_t *)(mod_base + l->fileofs);
4324         if (l->filelen % sizeof(*in))
4325                 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
4326         count = l->filelen / sizeof(*in);
4327         out = (rtexture_t **)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4328
4329         loadmodel->brushq3.data_lightmaps = out;
4330         loadmodel->brushq3.num_lightmaps = count;
4331
4332         for (i = 0;i < count;i++, in++, out++)
4333                 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
4334 }
4335
4336 static void Mod_Q3BSP_LoadFaces(lump_t *l)
4337 {
4338         q3dface_t *in, *oldin;
4339         msurface_t *out, *oldout;
4340         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;
4341         //int *originalelement3i;
4342         //int *originalneighbor3i;
4343         float *originalvertex3f;
4344         //float *originalsvector3f;
4345         //float *originaltvector3f;
4346         //float *originalnormal3f;
4347         float *originalcolor4f;
4348         float *originaltexcoordtexture2f;
4349         float *originaltexcoordlightmap2f;
4350         float *v;
4351         surfmesh_t *mesh, *tempmeshlist[1024];
4352
4353         in = (q3dface_t *)(mod_base + l->fileofs);
4354         if (l->filelen % sizeof(*in))
4355                 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
4356         count = l->filelen / sizeof(*in);
4357         out = (msurface_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4358
4359         loadmodel->data_surfaces = out;
4360         loadmodel->num_surfaces = count;
4361
4362         i = 0;
4363         for (meshnum = 0;i < count;meshnum++)
4364         {
4365                 oldi = i;
4366                 oldin = in;
4367                 oldout = out;
4368                 meshvertices = 0;
4369                 meshtriangles = 0;
4370                 for (;i < count;i++, in++, out++)
4371                 {
4372                         // check face type first
4373                         type = LittleLong(in->type);
4374                         if (type != Q3FACETYPE_POLYGON
4375                          && type != Q3FACETYPE_PATCH
4376                          && type != Q3FACETYPE_MESH
4377                          && type != Q3FACETYPE_FLARE)
4378                         {
4379                                 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, type);
4380                                 continue;
4381                         }
4382
4383                         n = LittleLong(in->textureindex);
4384                         if (n < 0 || n >= loadmodel->num_textures)
4385                         {
4386                                 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->num_textures);
4387                                 continue;
4388                         }
4389                         out->texture = loadmodel->data_textures + n;
4390                         n = LittleLong(in->effectindex);
4391                         if (n < -1 || n >= loadmodel->brushq3.num_effects)
4392                         {
4393                                 if (developer.integer >= 2)
4394                                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
4395                                 n = -1;
4396                         }
4397                         if (n == -1)
4398                                 out->effect = NULL;
4399                         else
4400                                 out->effect = loadmodel->brushq3.data_effects + n;
4401                         n = LittleLong(in->lightmapindex);
4402                         if (n >= loadmodel->brushq3.num_lightmaps)
4403                         {
4404                                 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
4405                                 n = -1;
4406                         }
4407                         else if (n < 0)
4408                                 n = -1;
4409                         if (n == -1)
4410                                 out->lightmaptexture = NULL;
4411                         else
4412                                 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
4413
4414                         firstvertex = LittleLong(in->firstvertex);
4415                         numvertices = LittleLong(in->numvertices);
4416                         firstelement = LittleLong(in->firstelement);
4417                         numtriangles = LittleLong(in->numelements) / 3;
4418                         if (numtriangles * 3 != LittleLong(in->numelements))
4419                         {
4420                                 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));
4421                                 continue;
4422                         }
4423                         if (firstvertex < 0 || firstvertex + numvertices > loadmodel->brushq3.num_vertices)
4424                         {
4425                                 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);
4426                                 continue;
4427                         }
4428                         if (firstelement < 0 || firstelement + numtriangles * 3 > loadmodel->brushq3.num_triangles * 3)
4429                         {
4430                                 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);
4431                                 continue;
4432                         }
4433                         switch(type)
4434                         {
4435                         case Q3FACETYPE_POLYGON:
4436                         case Q3FACETYPE_MESH:
4437                                 // no processing necessary
4438                                 break;
4439                         case Q3FACETYPE_PATCH:
4440                                 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
4441                                 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
4442                                 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))
4443                                 {
4444                                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
4445                                         continue;
4446                                 }
4447                                 originalvertex3f = loadmodel->brushq3.data_vertex3f + firstvertex * 3;
4448                                 // convert patch to Q3FACETYPE_MESH
4449                                 xtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
4450                                 ytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
4451                                 // bound to user settings
4452                                 xtess = bound(r_subdivisions_mintess.integer, xtess, r_subdivisions_maxtess.integer);
4453                                 ytess = bound(r_subdivisions_mintess.integer, ytess, r_subdivisions_maxtess.integer);
4454                                 // bound to sanity settings
4455                                 xtess = bound(1, xtess, 1024);
4456                                 ytess = bound(1, ytess, 1024);
4457                                 // bound to user limit on vertices
4458                                 while ((xtess > 1 || ytess > 1) && (((patchsize[0] - 1) * xtess) + 1) * (((patchsize[1] - 1) * ytess) + 1) > min(r_subdivisions_maxvertices.integer, 262144))
4459                                 {
4460                                         if (xtess > ytess)
4461                                                 xtess--;
4462                                         else
4463                                                 ytess--;
4464                                 }
4465                                 finalwidth = ((patchsize[0] - 1) * xtess) + 1;
4466                                 finalheight = ((patchsize[1] - 1) * ytess) + 1;
4467                                 numvertices = finalwidth * finalheight;
4468                                 numtriangles = (finalwidth - 1) * (finalheight - 1) * 2;
4469                                 break;
4470                         case Q3FACETYPE_FLARE:
4471                                 if (developer.integer >= 2)
4472                                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
4473                                 // don't render it
4474                                 continue;
4475                         }
4476                         out->num_vertices = numvertices;
4477                         out->num_triangles = numtriangles;
4478                         if (meshvertices + out->num_vertices > 65536)
4479                                 break;
4480                         meshvertices += out->num_vertices;
4481                         meshtriangles += out->num_triangles;
4482                 }
4483
4484                 i = oldi;
4485                 in = oldin;
4486                 out = oldout;
4487                 mesh = tempmeshlist[meshnum] = Mod_AllocSurfMesh(loadmodel->mempool, meshvertices, meshtriangles, false, true, false);
4488                 meshvertices = 0;
4489                 meshtriangles = 0;
4490                 for (;i < count && meshvertices + out->num_vertices <= mesh->num_vertices;i++, in++, out++)
4491                 {
4492                         if (out->num_vertices < 3 || out->num_triangles < 1)
4493                                 continue;
4494
4495                         type = LittleLong(in->type);
4496                         firstvertex = LittleLong(in->firstvertex);
4497                         firstelement = LittleLong(in->firstelement);
4498                         out->groupmesh = mesh;
4499                         out->num_firstvertex = meshvertices;
4500                         out->num_firsttriangle = meshtriangles;
4501                         switch(type)
4502                         {
4503                         case Q3FACETYPE_POLYGON:
4504                         case Q3FACETYPE_MESH:
4505                                 // no processing necessary
4506                                 for (j = 0;j < out->num_vertices;j++)
4507                                 {
4508                                         (out->groupmesh->data_vertex3f + 3 * out->num_firstvertex)[j * 3 + 0] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 0];
4509                                         (out->groupmesh->data_vertex3f + 3 * out->num_firstvertex)[j * 3 + 1] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 1];
4510                                         (out->groupmesh->data_vertex3f + 3 * out->num_firstvertex)[j * 3 + 2] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 2];
4511                                         (out->groupmesh->data_texcoordtexture2f + 2 * out->num_firstvertex)[j * 2 + 0] = loadmodel->brushq3.data_texcoordtexture2f[(firstvertex + j) * 2 + 0];
4512                                         (out->groupmesh->data_texcoordtexture2f + 2 * out->num_firstvertex)[j * 2 + 1] = loadmodel->brushq3.data_texcoordtexture2f[(firstvertex + j) * 2 + 1];
4513                                         (out->groupmesh->data_texcoordlightmap2f + 2 * out->num_firstvertex)[j * 2 + 0] = loadmodel->brushq3.data_texcoordlightmap2f[(firstvertex + j) * 2 + 0];
4514                                         (out->groupmesh->data_texcoordlightmap2f + 2 * out->num_firstvertex)[j * 2 + 1] = loadmodel->brushq3.data_texcoordlightmap2f[(firstvertex + j) * 2 + 1];
4515                                         (out->groupmesh->data_lightmapcolor4f + 4 * out->num_firstvertex)[j * 4 + 0] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 0];
4516                                         (out->groupmesh->data_lightmapcolor4f + 4 * out->num_firstvertex)[j * 4 + 1] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 1];
4517                                         (out->groupmesh->data_lightmapcolor4f + 4 * out->num_firstvertex)[j * 4 + 2] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 2];
4518                                         (out->groupmesh->data_lightmapcolor4f + 4 * out->num_firstvertex)[j * 4 + 3] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 3];
4519                                 }
4520                                 for (j = 0;j < out->num_triangles*3;j++)
4521                                         (out->groupmesh->data_element3i + 3 * out->num_firsttriangle)[j] = loadmodel->brushq3.data_element3i[firstelement + j] + out->num_firstvertex;
4522                                 break;
4523                         case Q3FACETYPE_PATCH:
4524                                 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
4525                                 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
4526                                 originalvertex3f = loadmodel->brushq3.data_vertex3f + firstvertex * 3;
4527                                 originaltexcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + firstvertex * 2;
4528                                 originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + firstvertex * 2;
4529                                 originalcolor4f = loadmodel->brushq3.data_color4f + firstvertex * 4;
4530                                 // convert patch to Q3FACETYPE_MESH
4531                                 xtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
4532                                 ytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
4533                                 // bound to user settings
4534                                 xtess = bound(r_subdivisions_mintess.integer, xtess, r_subdivisions_maxtess.integer);
4535                                 ytess = bound(r_subdivisions_mintess.integer, ytess, r_subdivisions_maxtess.integer);
4536                                 // bound to sanity settings
4537                                 xtess = bound(1, xtess, 1024);
4538                                 ytess = bound(1, ytess, 1024);
4539                                 // bound to user limit on vertices
4540                                 while ((xtess > 1 || ytess > 1) && (((patchsize[0] - 1) * xtess) + 1) * (((patchsize[1] - 1) * ytess) + 1) > min(r_subdivisions_maxvertices.integer, 262144))
4541                                 {
4542                                         if (xtess > ytess)
4543                                                 xtess--;
4544                                         else
4545                                                 ytess--;
4546                                 }
4547                                 finalwidth = ((patchsize[0] - 1) * xtess) + 1;
4548                                 finalheight = ((patchsize[1] - 1) * ytess) + 1;
4549                                 finalvertices = finalwidth * finalheight;
4550                                 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
4551                                 type = Q3FACETYPE_MESH;
4552                                 // generate geometry
4553                                 // (note: normals are skipped because they get recalculated)
4554                                 Q3PatchTesselateFloat(3, sizeof(float[3]), (out->groupmesh->data_vertex3f + 3 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[3]), originalvertex3f, xtess, ytess);
4555                                 Q3PatchTesselateFloat(2, sizeof(float[2]), (out->groupmesh->data_texcoordtexture2f + 2 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[2]), originaltexcoordtexture2f, xtess, ytess);
4556                                 Q3PatchTesselateFloat(2, sizeof(float[2]), (out->groupmesh->data_texcoordlightmap2f + 2 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[2]), originaltexcoordlightmap2f, xtess, ytess);
4557                                 Q3PatchTesselateFloat(4, sizeof(float[4]), (out->groupmesh->data_lightmapcolor4f + 4 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[4]), originalcolor4f, xtess, ytess);
4558                                 Q3PatchTriangleElements((out->groupmesh->data_element3i + 3 * out->num_firsttriangle), finalwidth, finalheight, out->num_firstvertex);
4559                                 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);
4560                                 if (developer.integer >= 2)
4561                                 {
4562                                         if (out->num_triangles < finaltriangles)
4563                                                 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);
4564                                         else
4565                                                 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);
4566                                 }
4567                                 // q3map does not put in collision brushes for curves... ugh
4568                                 // build the lower quality collision geometry
4569                                 xtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value);
4570                                 ytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value);
4571                                 // bound to user settings
4572                                 xtess = bound(r_subdivisions_collision_mintess.integer, xtess, r_subdivisions_collision_maxtess.integer);
4573                                 ytess = bound(r_subdivisions_collision_mintess.integer, ytess, r_subdivisions_collision_maxtess.integer);
4574                                 // bound to sanity settings
4575                                 xtess = bound(1, xtess, 1024);
4576                                 ytess = bound(1, ytess, 1024);
4577                                 // bound to user limit on vertices
4578                                 while ((xtess > 1 || ytess > 1) && (((patchsize[0] - 1) * xtess) + 1) * (((patchsize[1] - 1) * ytess) + 1) > min(r_subdivisions_collision_maxvertices.integer, 262144))
4579                                 {
4580                                         if (xtess > ytess)
4581                                                 xtess--;
4582                                         else
4583                                                 ytess--;
4584                                 }
4585                                 finalwidth = ((patchsize[0] - 1) * xtess) + 1;
4586                                 finalheight = ((patchsize[1] - 1) * ytess) + 1;
4587                                 finalvertices = finalwidth * finalheight;
4588                                 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
4589
4590                                 out->data_collisionvertex3f = (float *)Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * finalvertices);
4591                                 out->data_collisionelement3i = (int *)Mem_Alloc(loadmodel->mempool, sizeof(int[3]) * finaltriangles);
4592                                 out->num_collisionvertices = finalvertices;
4593                                 out->num_collisiontriangles = finaltriangles;
4594                                 Q3PatchTesselateFloat(3, sizeof(float[3]), out->data_collisionvertex3f, patchsize[0], patchsize[1], sizeof(float[3]), originalvertex3f, xtess, ytess);
4595                                 Q3PatchTriangleElements(out->data_collisionelement3i, finalwidth, finalheight, 0);
4596
4597                                 //Mod_SnapVertices(3, out->num_vertices, (out->groupmesh->data_vertex3f + 3 * out->num_firstvertex), 0.25);
4598                                 Mod_SnapVertices(3, out->num_collisionvertices, out->data_collisionvertex3f, 1);
4599
4600                                 oldnumtriangles = out->num_triangles;
4601                                 oldnumtriangles2 = out->num_collisiontriangles;
4602                                 out->num_collisiontriangles = Mod_RemoveDegenerateTriangles(out->num_collisiontriangles, out->data_collisionelement3i, out->data_collisionelement3i, out->data_collisionvertex3f);
4603                                 if (developer.integer)
4604                                         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);
4605                                 break;
4606                         default:
4607                                 break;
4608                         }
4609                         meshvertices += out->num_vertices;
4610                         meshtriangles += out->num_triangles;
4611                         for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++)
4612                                 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)
4613                                         invalidelements++;
4614                         if (invalidelements)
4615                         {
4616                                 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);
4617                                 for (j = 0;j < out->num_triangles * 3;j++)
4618                                 {
4619                                         Con_Printf(" %i", (out->groupmesh->data_element3i + 3 * out->num_firsttriangle)[j] - out->num_firstvertex);
4620                                         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)
4621                                                 (out->groupmesh->data_element3i + 3 * out->num_firsttriangle)[j] = out->num_firstvertex;
4622                                 }
4623                                 Con_Print("\n");
4624                         }
4625                         // for per pixel lighting
4626                         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);
4627                         // calculate a bounding box
4628                         VectorClear(out->mins);
4629                         VectorClear(out->maxs);
4630                         if (out->num_vertices)
4631                         {
4632                                 VectorCopy((out->groupmesh->data_vertex3f + 3 * out->num_firstvertex), out->mins);
4633                                 VectorCopy((out->groupmesh->data_vertex3f + 3 * out->num_firstvertex), out->maxs);
4634                                 for (j = 1, v = (out->groupmesh->data_vertex3f + 3 * out->num_firstvertex) + 3;j < out->num_vertices;j++, v += 3)
4635                                 {
4636                                         out->mins[0] = min(out->mins[0], v[0]);
4637                                         out->maxs[0] = max(out->maxs[0], v[0]);
4638                                         out->mins[1] = min(out->mins[1], v[1]);
4639                                         out->maxs[1] = max(out->maxs[1], v[1]);
4640                                         out->mins[2] = min(out->mins[2], v[2]);
4641                                         out->maxs[2] = max(out->maxs[2], v[2]);
4642                                 }
4643                                 out->mins[0] -= 1.0f;
4644                                 out->mins[1] -= 1.0f;
4645                                 out->mins[2] -= 1.0f;
4646                                 out->maxs[0] += 1.0f;
4647                                 out->maxs[1] += 1.0f;
4648                                 out->maxs[2] += 1.0f;
4649                         }
4650                         // set lightmap styles for consistency with q1bsp
4651                         //out->lightmapinfo->styles[0] = 0;
4652                         //out->lightmapinfo->styles[1] = 255;
4653                         //out->lightmapinfo->styles[2] = 255;
4654                         //out->lightmapinfo->styles[3] = 255;
4655                 }
4656         }
4657
4658         // now store the completed list of meshes
4659         loadmodel->nummeshes = meshnum;
4660         if (loadmodel->nummeshes)
4661         {
4662                 loadmodel->meshlist = (surfmesh_t **)Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t *) * loadmodel->nummeshes);
4663                 memcpy(loadmodel->meshlist, tempmeshlist, sizeof(surfmesh_t *) * loadmodel->nummeshes);
4664         }
4665
4666         // free the no longer needed vertex data
4667         loadmodel->brushq3.num_vertices = 0;
4668         Mem_Free(loadmodel->brushq3.data_vertex3f);
4669         loadmodel->brushq3.data_vertex3f = NULL;
4670         loadmodel->brushq3.data_texcoordtexture2f = NULL;
4671         loadmodel->brushq3.data_texcoordlightmap2f = NULL;
4672         loadmodel->brushq3.data_color4f = NULL;
4673         // free the no longer needed triangle data
4674         loadmodel->brushq3.num_triangles = 0;
4675         Mem_Free(loadmodel->brushq3.data_element3i);
4676         loadmodel->brushq3.data_element3i = NULL;
4677 }
4678
4679 static void Mod_Q3BSP_LoadModels(lump_t *l)
4680 {
4681         q3dmodel_t *in;
4682         q3dmodel_t *out;
4683         int i, j, n, c, count;
4684
4685         in = (q3dmodel_t *)(mod_base + l->fileofs);
4686         if (l->filelen % sizeof(*in))
4687                 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
4688         count = l->filelen / sizeof(*in);
4689         out = (q3dmodel_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4690
4691         loadmodel->brushq3.data_models = out;
4692         loadmodel->brushq3.num_models = count;
4693
4694         for (i = 0;i < count;i++, in++, out++)
4695         {
4696                 for (j = 0;j < 3;j++)
4697                 {
4698                         out->mins[j] = LittleFloat(in->mins[j]);
4699                         out->maxs[j] = LittleFloat(in->maxs[j]);
4700                 }
4701                 n = LittleLong(in->firstface);
4702                 c = LittleLong(in->numfaces);
4703                 if (n < 0 || n + c > loadmodel->num_surfaces)
4704                         Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)", n, n + c, loadmodel->num_surfaces);
4705                 out->firstface = n;
4706                 out->numfaces = c;
4707                 n = LittleLong(in->firstbrush);
4708                 c = LittleLong(in->numbrushes);
4709                 if (n < 0 || n + c > loadmodel->brush.num_brushes)
4710                         Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)", n, n + c, loadmodel->brush.num_brushes);
4711                 out->firstbrush = n;
4712                 out->numbrushes = c;
4713         }
4714 }
4715
4716 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
4717 {
4718         int *in;
4719         int *out;
4720         int i, n, count;
4721
4722         in = (int *)(mod_base + l->fileofs);
4723         if (l->filelen % sizeof(*in))
4724                 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
4725         count = l->filelen / sizeof(*in);
4726         out = (int *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4727
4728         loadmodel->brush.data_leafbrushes = out;
4729         loadmodel->brush.num_leafbrushes = count;
4730
4731         for (i = 0;i < count;i++, in++, out++)
4732         {
4733                 n = LittleLong(*in);
4734                 if (n < 0 || n >= loadmodel->brush.num_brushes)
4735                         Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)", n, loadmodel->brush.num_brushes);
4736                 *out = n;
4737         }
4738 }
4739
4740 static void Mod_Q3BSP_LoadLeafFaces(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_LoadLeafFaces: 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_leafsurfaces = out;
4753         loadmodel->brush.num_leafsurfaces = count;
4754
4755         for (i = 0;i < count;i++, in++, out++)
4756         {
4757                 n = LittleLong(*in);
4758                 if (n < 0 || n >= loadmodel->num_surfaces)
4759                         Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)", n, loadmodel->num_surfaces);
4760                 *out = n;
4761         }
4762 }
4763
4764 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
4765 {
4766         q3dleaf_t *in;
4767         mleaf_t *out;
4768         int i, j, n, c, count;
4769
4770         in = (q3dleaf_t *)(mod_base + l->fileofs);
4771         if (l->filelen % sizeof(*in))
4772                 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
4773         count = l->filelen / sizeof(*in);
4774         out = (mleaf_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4775
4776         loadmodel->brush.data_leafs = out;
4777         loadmodel->brush.num_leafs = count;
4778
4779         for (i = 0;i < count;i++, in++, out++)
4780         {
4781                 out->parent = NULL;
4782                 out->plane = NULL;
4783                 out->clusterindex = LittleLong(in->clusterindex);
4784                 out->areaindex = LittleLong(in->areaindex);
4785                 for (j = 0;j < 3;j++)
4786                 {
4787                         // yes the mins/maxs are ints
4788                         out->mins[j] = LittleLong(in->mins[j]) - 1;
4789                         out->maxs[j] = LittleLong(in->maxs[j]) + 1;
4790                 }
4791                 n = LittleLong(in->firstleafface);
4792                 c = LittleLong(in->numleaffaces);
4793                 if (n < 0 || n + c > loadmodel->brush.num_leafsurfaces)
4794                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafsurface range %i : %i (%i leafsurfaces)", n, n + c, loadmodel->brush.num_leafsurfaces);
4795                 out->firstleafsurface = loadmodel->brush.data_leafsurfaces + n;
4796                 out->numleafsurfaces = c;
4797                 n = LittleLong(in->firstleafbrush);
4798                 c = LittleLong(in->numleafbrushes);
4799                 if (n < 0 || n + c > loadmodel->brush.num_leafbrushes)
4800                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)", n, n + c, loadmodel->brush.num_leafbrushes);
4801                 out->firstleafbrush = loadmodel->brush.data_leafbrushes + n;
4802                 out->numleafbrushes = c;
4803         }
4804 }
4805
4806 static void Mod_Q3BSP_LoadNodes(lump_t *l)
4807 {
4808         q3dnode_t *in;
4809         mnode_t *out;
4810         int i, j, n, count;
4811
4812         in = (q3dnode_t *)(mod_base + l->fileofs);
4813         if (l->filelen % sizeof(*in))
4814                 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
4815         count = l->filelen / sizeof(*in);
4816         out = (mnode_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4817
4818         loadmodel->brush.data_nodes = out;
4819         loadmodel->brush.num_nodes = count;
4820
4821         for (i = 0;i < count;i++, in++, out++)
4822         {
4823                 out->parent = NULL;
4824                 n = LittleLong(in->planeindex);
4825                 if (n < 0 || n >= loadmodel->brush.num_planes)
4826                         Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)", n, loadmodel->brush.num_planes);
4827                 out->plane = loadmodel->brush.data_planes + n;
4828                 for (j = 0;j < 2;j++)
4829                 {
4830                         n = LittleLong(in->childrenindex[j]);
4831                         if (n >= 0)
4832                         {
4833                                 if (n >= loadmodel->brush.num_nodes)
4834                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)", n, loadmodel->brush.num_nodes);
4835                                 out->children[j] = loadmodel->brush.data_nodes + n;
4836                         }
4837                         else
4838                         {
4839                                 n = -1 - n;
4840                                 if (n >= loadmodel->brush.num_leafs)
4841                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)", n, loadmodel->brush.num_leafs);
4842                                 out->children[j] = (mnode_t *)(loadmodel->brush.data_leafs + n);
4843                         }
4844                 }
4845                 for (j = 0;j < 3;j++)
4846                 {
4847                         // yes the mins/maxs are ints
4848                         out->mins[j] = LittleLong(in->mins[j]) - 1;
4849                         out->maxs[j] = LittleLong(in->maxs[j]) + 1;
4850                 }
4851         }
4852
4853         // set the parent pointers
4854         Mod_Q1BSP_LoadNodes_RecursiveSetParent(loadmodel->brush.data_nodes, NULL);
4855 }
4856
4857 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
4858 {
4859         q3dlightgrid_t *in;
4860         q3dlightgrid_t *out;
4861         int count;
4862
4863         in = (q3dlightgrid_t *)(mod_base + l->fileofs);
4864         if (l->filelen % sizeof(*in))
4865                 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
4866         loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
4867         loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
4868         loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
4869         loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4870         loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4871         loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4872         loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4873         loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4874         loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4875         loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4876         loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4877         loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4878         count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4879         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]);
4880         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]);
4881
4882         // if lump is empty there is nothing to load, we can deal with that in the LightPoint code
4883         if (l->filelen)
4884         {
4885                 if (l->filelen < count * (int)sizeof(*in))
4886                         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]);
4887                 if (l->filelen != count * (int)sizeof(*in))
4888                         Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i", count * sizeof(*in), l->filelen);
4889                 out = (q3dlightgrid_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4890                 loadmodel->brushq3.data_lightgrid = out;
4891                 loadmodel->brushq3.num_lightgrid = count;
4892                 // no swapping or validation necessary
4893                 memcpy(out, in, count * (int)sizeof(*out));
4894         }
4895 }
4896
4897 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4898 {
4899         q3dpvs_t *in;
4900         int totalchains;
4901
4902         if (l->filelen == 0)
4903         {
4904                 int i;
4905                 // unvised maps often have cluster indices even without pvs, so check
4906                 // leafs to find real number of clusters
4907                 loadmodel->brush.num_pvsclusters = 1;
4908                 for (i = 0;i < loadmodel->brush.num_leafs;i++)
4909                         loadmodel->brush.num_pvsclusters = max(loadmodel->brush.num_pvsclusters, loadmodel->brush.data_leafs[i].clusterindex + 1);
4910
4911                 // create clusters
4912                 loadmodel->brush.num_pvsclusterbytes = (loadmodel->brush.num_pvsclusters + 7) / 8;
4913                 totalchains = loadmodel->brush.num_pvsclusterbytes * loadmodel->brush.num_pvsclusters;
4914                 loadmodel->brush.data_pvsclusters = (unsigned char *)Mem_Alloc(loadmodel->mempool, totalchains);
4915                 memset(loadmodel->brush.data_pvsclusters, 0xFF, totalchains);
4916                 return;
4917         }
4918
4919         in = (q3dpvs_t *)(mod_base + l->fileofs);
4920         if (l->filelen < 9)
4921                 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4922
4923         loadmodel->brush.num_pvsclusters = LittleLong(in->numclusters);
4924         loadmodel->brush.num_pvsclusterbytes = LittleLong(in->chainlength);
4925         if (loadmodel->brush.num_pvsclusterbytes < ((loadmodel->brush.num_pvsclusters + 7) / 8))
4926                 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8", loadmodel->brush.num_pvsclusterbytes, loadmodel->brush.num_pvsclusters);
4927         totalchains = loadmodel->brush.num_pvsclusterbytes * loadmodel->brush.num_pvsclusters;
4928         if (l->filelen < totalchains + (int)sizeof(*in))
4929                 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);
4930
4931         loadmodel->brush.data_pvsclusters = (unsigned char *)Mem_Alloc(loadmodel->mempool, totalchains);
4932         memcpy(loadmodel->brush.data_pvsclusters, (unsigned char *)(in + 1), totalchains);
4933 }
4934
4935 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4936 {
4937         int i, j, k, index[3];
4938         float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4939         q3dlightgrid_t *a, *s;
4940         if (!model->brushq3.num_lightgrid)
4941         {
4942                 ambientcolor[0] = 1;
4943                 ambientcolor[1] = 1;
4944                 ambientcolor[2] = 1;
4945                 return;
4946         }
4947         Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4948         //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4949         //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4950         transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0] - 1);
4951         transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1] - 1);
4952         transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2] - 1);
4953         index[0] = (int)floor(transformed[0]);
4954         index[1] = (int)floor(transformed[1]);
4955         index[2] = (int)floor(transformed[2]);
4956         //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4957         // now lerp the values
4958         VectorClear(diffusenormal);
4959         a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4960         for (k = 0;k < 2;k++)
4961         {
4962                 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4963                 if (blend1 < 0.001f || index[2] + k >= model->brushq3.num_lightgrid_isize[2])
4964                         continue;
4965                 for (j = 0;j < 2;j++)
4966                 {
4967                         blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4968                         if (blend2 < 0.001f || index[1] + j >= model->brushq3.num_lightgrid_isize[1])
4969                                 continue;
4970                         for (i = 0;i < 2;i++)
4971                         {
4972                                 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4973                                 if (blend < 0.001f || index[0] + i >= model->brushq3.num_lightgrid_isize[0])
4974                                         continue;
4975                                 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4976                                 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4977                                 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4978                                 pitch = s->diffusepitch * M_PI / 128;
4979                                 yaw = s->diffuseyaw * M_PI / 128;
4980                                 sinpitch = sin(pitch);
4981                                 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4982                                 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4983                                 diffusenormal[2] += blend * (cos(pitch));
4984                                 //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)));
4985                         }
4986                 }
4987         }
4988         VectorNormalize(diffusenormal);
4989         //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]);
4990 }
4991
4992 static void Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace_t *trace, model_t *model, mnode_t *node, const vec3_t point, int markframe)
4993 {
4994         int i;
4995         mleaf_t *leaf;
4996         colbrushf_t *brush;
4997         // find which leaf the point is in
4998         while (node->plane)
4999                 node = node->children[DotProduct(point, node->plane->normal) < node->plane->dist];
5000         // point trace the brushes
5001         leaf = (mleaf_t *)node;
5002         for (i = 0;i < leaf->numleafbrushes;i++)
5003         {
5004                 brush = model->brush.data_brushes[leaf->firstleafbrush[i]].colbrushf;
5005                 if (brush && brush->markframe != markframe && BoxesOverlap(point, point, brush->mins, brush->maxs))
5006                 {
5007                         brush->markframe = markframe;
5008                         Collision_TracePointBrushFloat(trace, point, brush);
5009                 }
5010         }
5011         // can't do point traces on curves (they have no thickness)
5012 }
5013
5014 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)
5015 {
5016         int i, startside, endside;
5017         float dist1, dist2, midfrac, mid[3], nodesegmentmins[3], nodesegmentmaxs[3];
5018         mleaf_t *leaf;
5019         msurface_t *surface;
5020         colbrushf_t *brush;
5021         if (startfrac > trace->realfraction)
5022                 return;
5023         // note: all line fragments past first impact fraction are ignored
5024         if (VectorCompare(start, end))
5025         {
5026                 // find which leaf the point is in
5027                 while (node->plane)
5028                         node = node->children[DotProduct(start, node->plane->normal) < node->plane->dist];
5029         }
5030         else
5031         {
5032                 // find which nodes the line is in and recurse for them
5033                 while (node->plane)
5034                 {
5035                         // recurse down node sides
5036                         dist1 = PlaneDiff(start, node->plane);
5037                         dist2 = PlaneDiff(end, node->plane);
5038                         startside = dist1 < 0;
5039                         endside = dist2 < 0;
5040                         if (startside == endside)
5041                         {
5042                                 // most of the time the line fragment is on one side of the plane
5043                                 node = node->children[startside];
5044                         }
5045                         else
5046                         {
5047                                 // line crosses node plane, split the line
5048                                 midfrac = dist1 / (dist1 - dist2);
5049                                 VectorLerp(start, midfrac, end, mid);
5050                                 // take the near side first
5051                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
5052                                 if (midfrac <= trace->realfraction)
5053                                         Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
5054                                 return;
5055                         }
5056                 }
5057         }
5058         // hit a leaf
5059         nodesegmentmins[0] = min(start[0], end[0]);
5060         nodesegmentmins[1] = min(start[1], end[1]);
5061         nodesegmentmins[2] = min(start[2], end[2]);
5062         nodesegmentmaxs[0] = max(start[0], end[0]);
5063         nodesegmentmaxs[1] = max(start[1], end[1]);
5064         nodesegmentmaxs[2] = max(start[2], end[2]);
5065         // line trace the brushes
5066         leaf = (mleaf_t *)node;
5067         for (i = 0;i < leaf->numleafbrushes;i++)
5068         {
5069                 brush = model->brush.data_brushes[leaf->firstleafbrush[i]].colbrushf;
5070                 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
5071                 {
5072                         brush->markframe = markframe;
5073                         Collision_TraceLineBrushFloat(trace, linestart, lineend, brush, brush);
5074                         if (startfrac > trace->realfraction)
5075                                 return;
5076                 }
5077         }
5078         // can't do point traces on curves (they have no thickness)
5079         if (mod_q3bsp_curves_collisions.integer && !VectorCompare(start, end))
5080         {
5081                 // line trace the curves
5082                 for (i = 0;i < leaf->numleafsurfaces;i++)
5083                 {
5084                         surface = model->data_surfaces + leaf->firstleafsurface[i];
5085                         if (surface->num_collisiontriangles && surface->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, surface->mins, surface->maxs))
5086                         {
5087                                 surface->collisionmarkframe = markframe;
5088                                 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, segmentmins, segmentmaxs);
5089                                 if (startfrac > trace->realfraction)
5090                                         return;
5091                         }
5092                 }
5093         }
5094 }
5095
5096 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)
5097 {
5098         int i;
5099         int sides;
5100         mleaf_t *leaf;
5101         colbrushf_t *brush;
5102         msurface_t *surface;
5103         mplane_t *plane;
5104         float nodesegmentmins[3], nodesegmentmaxs[3];
5105 #if 1
5106         for (;;)
5107         {
5108                 plane = node->plane;
5109                 if (!plane)
5110                         break;
5111                 // axial planes are much more common than non-axial, so an optimized
5112                 // axial case pays off here
5113                 // we must handle sides == 0 because of NANs (crashing is far worse than recursing the entire tree!)
5114                 if (plane->type < 3)
5115                 {
5116                         // this is an axial plane, compare bounding box directly to it and
5117                         // recurse sides accordingly
5118                         // recurse down node sides
5119                         // use an inlined axial BoxOnPlaneSide to slightly reduce overhead
5120                         //sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, plane);
5121                         //sides = ((segmentmaxs[plane->type] >= plane->dist) | ((segmentmins[plane->type] < plane->dist) << 1));
5122                         sides = ((segmentmaxs[plane->type] >= plane->dist) + ((segmentmins[plane->type] < plane->dist) * 2));
5123                         if (sides == 3)
5124                         {
5125                                 // segment box crosses plane
5126                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5127                                 sides = 2;
5128                         }
5129                 }
5130                 else
5131                 {
5132                         // this is a non-axial plane, entire trace bounding box
5133                         // comparisons against it are likely to be very sloppy, so in if
5134                         // the whole box is split by the plane we then test the start/end
5135                         // boxes against it to be sure
5136                         sides = BoxOnPlaneSide(segmentmins, segmentmaxs, plane);
5137                         if (sides == 3)
5138                         {
5139                                 // segment box crosses plane
5140                                 // now check start and end brush boxes to handle a lot of 'diagonal' cases more efficiently...
5141                                 sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, plane);
5142                                 if (sides == 3)
5143                                 {
5144                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5145                                         sides = 2;
5146                                 }
5147                         }
5148                 }
5149                 if (sides == 0)
5150                         return; // ERROR: NAN bounding box!
5151                 // take whichever side the segment box is on
5152                 //if (sides < 1 || sides > 2)
5153                 //      Sys_Error("Mod_Q3BSP_TraceBrush_RecursiveBSPNode: side > 2\n");
5154                 node = node->children[sides - 1];
5155         }
5156         nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5157         nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5158         nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5159         nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5160         nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5161         nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5162 #elif 1
5163         for (;;)
5164         {
5165                 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5166                 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5167                 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5168                 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5169                 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5170                 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5171                 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
5172                         return;
5173                 if (!node->plane)
5174                         break;
5175                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5176                 node = node->children[1];
5177         }
5178 #elif 0
5179         // FIXME: could be made faster by copying TraceLine code and making it use
5180         // box plane distances...  (variant on the BoxOnPlaneSide code)
5181         for (;;)
5182         {
5183                 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5184                 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5185                 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5186                 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5187                 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5188                 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5189                 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
5190                         return;
5191                 if (!node->plane)
5192                         break;
5193                 if (mod_q3bsp_debugtracebrush.integer == 2)
5194                 {
5195                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5196                         node = node->children[1];
5197                         continue;
5198                 }
5199                 else if (mod_q3bsp_debugtracebrush.integer == 1)
5200                 {
5201                         // recurse down node sides
5202                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5203                         if (sides == 3)
5204                         {
5205                                 // segment box crosses plane
5206                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5207                                 node = node->children[1];
5208                                 continue;
5209                         }
5210                         if (sides == 0)
5211                                 return; // ERROR: NAN bounding box!
5212                         // take whichever side the segment box is on
5213                         node = node->children[sides - 1];
5214                         continue;
5215                 }
5216                 else
5217                 {
5218                         // recurse down node sides
5219                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5220                         if (sides == 3)
5221                         {
5222                                 // segment box crosses plane
5223                                 // now check start and end brush boxes to handle a lot of 'diagonal' cases more efficiently...
5224                                 sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, node->plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, node->plane);
5225                                 if (sides == 3)
5226                                 {
5227                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5228                                         node = node->children[1];
5229                                         continue;
5230                                 }
5231                         }
5232                         if (sides == 0)
5233                                 return; // ERROR: NAN bounding box!
5234                         // take whichever side the segment box is on
5235                         node = node->children[sides - 1];
5236                         continue;
5237                 }
5238                 return;
5239         }
5240 #else
5241         // FIXME: could be made faster by copying TraceLine code and making it use
5242         // box plane distances...  (variant on the BoxOnPlaneSide code)
5243         for (;;)
5244         {
5245                 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5246                 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5247                 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5248                 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5249                 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5250                 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5251                 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
5252                         return;
5253                 if (!node->plane)
5254                         break;
5255                 if (mod_q3bsp_debugtracebrush.integer == 2)
5256                 {
5257                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5258                         node = node->children[1];
5259                 }
5260                 else if (mod_q3bsp_debugtracebrush.integer == 1)
5261                 {
5262                         // recurse down node sides
5263                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5264                         if (sides == 3)
5265                         {
5266                                 // segment box crosses plane
5267                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5268                                 node = node->children[1];
5269                         }
5270                         else
5271                         {
5272                                 if (sides == 0)
5273                                         return; // ERROR: NAN bounding box!
5274                                 // take whichever side the segment box is on
5275                                 node = node->children[sides - 1];
5276                         }
5277                 }
5278                 else
5279                 {
5280                         // recurse down node sides
5281                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5282                         if (sides == 3)
5283                         {
5284                                 // segment box crosses plane
5285                                 // now check start and end brush boxes to handle a lot of 'diagonal' cases more efficiently...
5286                                 sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, node->plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, node->plane);
5287                                 if (sides == 3)
5288                                 {
5289                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5290                                         sides = 2;
5291                                 }
5292                         }
5293                         if (sides == 0)
5294                                 return; // ERROR: NAN bounding box!
5295                         // take whichever side the segment box is on
5296                         node = node->children[sides - 1];
5297                 }
5298         }
5299 #endif
5300         // hit a leaf
5301         leaf = (mleaf_t *)node;
5302         for (i = 0;i < leaf->numleafbrushes;i++)
5303         {
5304                 brush = model->brush.data_brushes[leaf->firstleafbrush[i]].colbrushf;
5305                 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
5306                 {
5307                         brush->markframe = markframe;
5308                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, brush, brush);
5309                 }
5310         }
5311         if (mod_q3bsp_curves_collisions.integer)
5312         {
5313                 for (i = 0;i < leaf->numleafsurfaces;i++)
5314                 {
5315                         surface = model->data_surfaces + leaf->firstleafsurface[i];
5316                         if (surface->num_collisiontriangles && surface->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, surface->mins, surface->maxs))
5317                         {
5318                                 surface->collisionmarkframe = markframe;
5319                                 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, segmentmins, segmentmaxs);
5320                         }
5321                 }
5322         }
5323 }
5324
5325 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)
5326 {
5327         int i;
5328         float segmentmins[3], segmentmaxs[3];
5329         colbrushf_t *thisbrush_start, *thisbrush_end;
5330         matrix4x4_t startmatrix, endmatrix;
5331         static int markframe = 0;
5332         msurface_t *surface;
5333         q3mbrush_t *brush;
5334         memset(trace, 0, sizeof(*trace));
5335         trace->fraction = 1;
5336         trace->realfraction = 1;
5337         trace->hitsupercontentsmask = hitsupercontentsmask;
5338         Matrix4x4_CreateIdentity(&startmatrix);
5339         Matrix4x4_CreateIdentity(&endmatrix);
5340         segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
5341         segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
5342         segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
5343         segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
5344         segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
5345         segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
5346         if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
5347         {
5348                 if (VectorCompare(boxstartmins, boxendmins))
5349                 {
5350                         // point trace
5351                         if (model->brush.submodel)
5352                         {
5353                                 for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
5354                                         if (brush->colbrushf)
5355                                                 Collision_TracePointBrushFloat(trace, boxstartmins, brush->colbrushf);
5356                         }
5357                         else
5358                                 Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace, model, model->brush.data_nodes, boxstartmins, ++markframe);
5359                 }
5360                 else
5361                 {
5362                         // line trace
5363                         if (model->brush.submodel)
5364                         {
5365                                 for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
5366                                         if (brush->colbrushf)
5367                                                 Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, brush->colbrushf, brush->colbrushf);
5368                                 if (mod_q3bsp_curves_collisions.integer)
5369                                         for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
5370                                                 if (surface->num_collisiontriangles)
5371                                                         Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, segmentmins, segmentmaxs);
5372                         }
5373                         else
5374                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, model->brush.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe, segmentmins, segmentmaxs);
5375                 }
5376         }
5377         else
5378         {
5379                 // box trace, performed as brush trace
5380                 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
5381                 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
5382                 if (model->brush.submodel)
5383                 {
5384                         for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
5385                                 if (brush->colbrushf)
5386                                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, brush->colbrushf, brush->colbrushf);
5387                         if (mod_q3bsp_curves_collisions.integer)
5388                                 for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
5389                                         if (surface->num_collisiontriangles)
5390                                                 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, segmentmins, segmentmaxs);
5391                 }
5392                 else
5393                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, model->brush.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
5394         }
5395 }
5396
5397 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
5398 {
5399         int supercontents = 0;
5400         if (nativecontents & CONTENTSQ3_SOLID)
5401                 supercontents |= SUPERCONTENTS_SOLID;
5402         if (nativecontents & CONTENTSQ3_WATER)
5403                 supercontents |= SUPERCONTENTS_WATER;
5404         if (nativecontents & CONTENTSQ3_SLIME)
5405                 supercontents |= SUPERCONTENTS_SLIME;
5406         if (nativecontents & CONTENTSQ3_LAVA)
5407                 supercontents |= SUPERCONTENTS_LAVA;
5408         if (nativecontents & CONTENTSQ3_BODY)
5409                 supercontents |= SUPERCONTENTS_BODY;
5410         if (nativecontents & CONTENTSQ3_CORPSE)
5411                 supercontents |= SUPERCONTENTS_CORPSE;
5412         if (nativecontents & CONTENTSQ3_NODROP)
5413                 supercontents |= SUPERCONTENTS_NODROP;
5414         if (nativecontents & CONTENTSQ3_PLAYERCLIP)
5415                 supercontents |= SUPERCONTENTS_PLAYERCLIP;
5416         if (nativecontents & CONTENTSQ3_MONSTERCLIP)
5417                 supercontents |= SUPERCONTENTS_MONSTERCLIP;
5418         if (nativecontents & CONTENTSQ3_DONOTENTER)
5419                 supercontents |= SUPERCONTENTS_DONOTENTER;
5420         return supercontents;
5421 }
5422
5423 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
5424 {
5425         int nativecontents = 0;
5426         if (supercontents & SUPERCONTENTS_SOLID)
5427                 nativecontents |= CONTENTSQ3_SOLID;
5428         if (supercontents & SUPERCONTENTS_WATER)
5429                 nativecontents |= CONTENTSQ3_WATER;
5430         if (supercontents & SUPERCONTENTS_SLIME)
5431                 nativecontents |= CONTENTSQ3_SLIME;
5432         if (supercontents & SUPERCONTENTS_LAVA)
5433                 nativecontents |= CONTENTSQ3_LAVA;
5434         if (supercontents & SUPERCONTENTS_BODY)
5435                 nativecontents |= CONTENTSQ3_BODY;
5436         if (supercontents & SUPERCONTENTS_CORPSE)
5437                 nativecontents |= CONTENTSQ3_CORPSE;
5438         if (supercontents & SUPERCONTENTS_NODROP)
5439                 nativecontents |= CONTENTSQ3_NODROP;
5440         if (supercontents & SUPERCONTENTS_PLAYERCLIP)
5441                 nativecontents |= CONTENTSQ3_PLAYERCLIP;
5442         if (supercontents & SUPERCONTENTS_MONSTERCLIP)
5443                 nativecontents |= CONTENTSQ3_MONSTERCLIP;
5444         if (supercontents & SUPERCONTENTS_DONOTENTER)
5445                 nativecontents |= CONTENTSQ3_DONOTENTER;
5446         return nativecontents;
5447 }
5448
5449 void Mod_Q3BSP_RecursiveFindNumLeafs(mnode_t *node)
5450 {
5451         int numleafs;
5452         while (node->plane)
5453         {
5454                 Mod_Q3BSP_RecursiveFindNumLeafs(node->children[0]);
5455                 node = node->children[1];
5456         }
5457         numleafs = ((mleaf_t *)node - loadmodel->brush.data_leafs) + 1;
5458         if (loadmodel->brush.num_leafs < numleafs)
5459                 loadmodel->brush.num_leafs = numleafs;
5460 }
5461
5462 void Mod_Q3BSP_Load(model_t *mod, void *buffer, void *bufferend)
5463 {
5464         int i, j, numshadowmeshtriangles;
5465         q3dheader_t *header;
5466         float corner[3], yawradius, modelradius;
5467         msurface_t *surface;
5468
5469         mod->type = mod_brushq3;
5470         mod->numframes = 2; // although alternate textures are not supported it is annoying to complain about no such frame 1
5471         mod->numskins = 1;
5472
5473         header = (q3dheader_t *)buffer;
5474
5475         i = LittleLong(header->version);
5476         if (i != Q3BSPVERSION)
5477                 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
5478         mod->brush.ishlbsp = false;
5479         mod->brush.ismcbsp = false;
5480         if (loadmodel->isworldmodel)
5481         {
5482                 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
5483                 Cvar_SetValue("mcbsp", mod->brush.ismcbsp);
5484         }
5485
5486         mod->soundfromcenter = true;
5487         mod->TraceBox = Mod_Q3BSP_TraceBox;
5488         mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
5489         mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
5490         mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
5491         mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
5492         mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
5493         mod->brush.BoxTouchingLeafPVS = Mod_Q1BSP_BoxTouchingLeafPVS;
5494         mod->brush.BoxTouchingVisibleLeafs = Mod_Q1BSP_BoxTouchingVisibleLeafs;
5495         mod->brush.FindBoxClusters = Mod_Q1BSP_FindBoxClusters;
5496         mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
5497         mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
5498         mod->brush.PointInLeaf = Mod_Q1BSP_PointInLeaf;
5499         mod->Draw = R_Q1BSP_Draw;
5500         mod->GetLightInfo = R_Q1BSP_GetLightInfo;
5501         mod->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
5502         mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
5503         mod->DrawLight = R_Q1BSP_DrawLight;
5504
5505         mod_base = (unsigned char *)header;
5506
5507         // swap all the lumps
5508         header->ident = LittleLong(header->ident);
5509         header->version = LittleLong(header->version);
5510         for (i = 0;i < Q3HEADER_LUMPS;i++)
5511         {
5512                 header->lumps[i].fileofs = LittleLong(header->lumps[i].fileofs);
5513                 header->lumps[i].filelen = LittleLong(header->lumps[i].filelen);
5514         }
5515
5516         Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
5517         Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
5518         Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
5519         Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
5520         Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
5521         Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
5522         Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
5523         Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
5524         Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
5525         Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
5526         Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
5527         Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
5528         Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
5529         Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
5530         Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
5531         Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
5532         Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
5533         loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
5534
5535         // the MakePortals code works fine on the q3bsp data as well
5536         Mod_Q1BSP_MakePortals();
5537
5538         // make a single combined shadow mesh to allow optimized shadow volume creation
5539         numshadowmeshtriangles = 0;
5540         for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
5541         {
5542                 surface->num_firstshadowmeshtriangle = numshadowmeshtriangles;
5543                 numshadowmeshtriangles += surface->num_triangles;
5544         }
5545         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
5546         for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
5547                 if (surface->groupmesh)
5548                         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));
5549         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true);
5550         Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
5551
5552         loadmodel->brush.num_leafs = 0;
5553         Mod_Q3BSP_RecursiveFindNumLeafs(loadmodel->brush.data_nodes);
5554
5555         if (loadmodel->isworldmodel)
5556         {
5557                 // clear out any stale submodels or worldmodels lying around
5558                 // if we did this clear before now, an error might abort loading and
5559                 // leave things in a bad state
5560                 Mod_RemoveStaleWorldModels(loadmodel);
5561         }
5562
5563         mod = loadmodel;
5564         for (i = 0;i < loadmodel->brush.numsubmodels;i++)
5565         {
5566                 if (i > 0)
5567                 {
5568                         char name[10];
5569                         // LordHavoc: only register submodels if it is the world
5570                         // (prevents external bsp models from replacing world submodels with
5571                         //  their own)
5572                         if (!loadmodel->isworldmodel)
5573                                 continue;
5574                         // duplicate the basic information
5575                         sprintf(name, "*%i", i);
5576                         mod = Mod_FindName(name);
5577                         *mod = *loadmodel;
5578                         strcpy(mod->name, name);
5579                         // textures and memory belong to the main model
5580                         mod->texturepool = NULL;
5581                         mod->mempool = NULL;
5582                         mod->brush.GetPVS = NULL;
5583                         mod->brush.FatPVS = NULL;
5584                         mod->brush.BoxTouchingPVS = NULL;
5585                         mod->brush.BoxTouchingLeafPVS = NULL;
5586                         mod->brush.BoxTouchingVisibleLeafs = NULL;
5587                         mod->brush.FindBoxClusters = NULL;
5588                         mod->brush.LightPoint = NULL;
5589                         mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
5590                 }
5591                 mod->brush.submodel = i;
5592
5593                 // make the model surface list (used by shadowing/lighting)
5594                 mod->firstmodelsurface = mod->brushq3.data_models[i].firstface;
5595                 mod->nummodelsurfaces = mod->brushq3.data_models[i].numfaces;
5596                 mod->firstmodelbrush = mod->brushq3.data_models[i].firstbrush;
5597                 mod->nummodelbrushes = mod->brushq3.data_models[i].numbrushes;
5598                 mod->surfacelist = (int *)Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->surfacelist));
5599                 for (j = 0;j < mod->nummodelsurfaces;j++)
5600                         mod->surfacelist[j] = mod->firstmodelsurface + j;
5601
5602                 VectorCopy(mod->brushq3.data_models[i].mins, mod->normalmins);
5603                 VectorCopy(mod->brushq3.data_models[i].maxs, mod->normalmaxs);
5604                 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
5605                 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
5606                 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
5607                 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
5608                 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
5609                 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
5610                 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
5611                 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
5612                 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
5613                 mod->yawmins[2] = mod->normalmins[2];
5614                 mod->yawmaxs[2] = mod->normalmaxs[2];
5615                 mod->radius = modelradius;
5616                 mod->radius2 = modelradius * modelradius;
5617
5618                 for (j = 0;j < mod->nummodelsurfaces;j++)
5619                         if (mod->data_surfaces[j + mod->firstmodelsurface].texture->surfaceflags & Q3SURFACEFLAG_SKY)
5620                                 break;
5621                 if (j < mod->nummodelsurfaces)
5622                         mod->DrawSky = R_Q1BSP_DrawSky;
5623         }
5624 }
5625
5626 void Mod_IBSP_Load(model_t *mod, void *buffer, void *bufferend)
5627 {
5628         int i = LittleLong(((int *)buffer)[1]);
5629         if (i == Q3BSPVERSION)
5630                 Mod_Q3BSP_Load(mod,buffer, bufferend);
5631         else if (i == Q2BSPVERSION)
5632                 Mod_Q2BSP_Load(mod,buffer, bufferend);
5633         else
5634                 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i", i);
5635 }
5636
5637 void Mod_MAP_Load(model_t *mod, void *buffer, void *bufferend)
5638 {
5639         Host_Error("Mod_MAP_Load: not yet implemented");
5640 }
5641