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