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