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