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