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