]> icculus.org git repositories - divverent/darkplaces.git/blob - model_brush.c
forgot one part of the shadowmesh fix
[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, true);
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, void *bufferend)
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                 if (surface->texture->basematerialflags & MATERIALFLAG_SOLID)
3012                 {
3013                         surface->num_firstshadowmeshtriangle = numshadowmeshtriangles;
3014                         numshadowmeshtriangles += surface->num_triangles;
3015                 }
3016         }
3017         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
3018         for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
3019                 if (surface->texture->basematerialflags & MATERIALFLAG_SOLID)
3020                         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));
3021         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true);
3022         Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
3023
3024         if (loadmodel->brush.numsubmodels)
3025                 loadmodel->brush.submodels = Mem_Alloc(loadmodel->mempool, loadmodel->brush.numsubmodels * sizeof(model_t *));
3026
3027         if (loadmodel->isworldmodel)
3028         {
3029                 // clear out any stale submodels or worldmodels lying around
3030                 // if we did this clear before now, an error might abort loading and
3031                 // leave things in a bad state
3032                 Mod_RemoveStaleWorldModels(loadmodel);
3033         }
3034
3035         // LordHavoc: to clear the fog around the original quake submodel code, I
3036         // will explain:
3037         // first of all, some background info on the submodels:
3038         // model 0 is the map model (the world, named maps/e1m1.bsp for example)
3039         // model 1 and higher are submodels (doors and the like, named *1, *2, etc)
3040         // now the weird for loop itself:
3041         // the loop functions in an odd way, on each iteration it sets up the
3042         // current 'mod' model (which despite the confusing code IS the model of
3043         // the number i), at the end of the loop it duplicates the model to become
3044         // the next submodel, and loops back to set up the new submodel.
3045
3046         // LordHavoc: now the explanation of my sane way (which works identically):
3047         // set up the world model, then on each submodel copy from the world model
3048         // and set up the submodel with the respective model info.
3049         for (i = 0;i < mod->brush.numsubmodels;i++)
3050         {
3051                 // LordHavoc: this code was originally at the end of this loop, but
3052                 // has been transformed to something more readable at the start here.
3053
3054                 if (i > 0)
3055                 {
3056                         char name[10];
3057                         // LordHavoc: only register submodels if it is the world
3058                         // (prevents external bsp models from replacing world submodels with
3059                         //  their own)
3060                         if (!loadmodel->isworldmodel)
3061                                 continue;
3062                         // duplicate the basic information
3063                         sprintf(name, "*%i", i);
3064                         mod = Mod_FindName(name);
3065                         // copy the base model to this one
3066                         *mod = *loadmodel;
3067                         // rename the clone back to its proper name
3068                         strcpy(mod->name, name);
3069                         // textures and memory belong to the main model
3070                         mod->texturepool = NULL;
3071                         mod->mempool = NULL;
3072                 }
3073
3074                 mod->brush.submodel = i;
3075
3076                 if (loadmodel->brush.submodels)
3077                         loadmodel->brush.submodels[i] = mod;
3078
3079                 bm = &mod->brushq1.submodels[i];
3080
3081                 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
3082                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
3083                 {
3084                         mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
3085                         mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
3086                 }
3087
3088                 mod->firstmodelsurface = bm->firstface;
3089                 mod->nummodelsurfaces = bm->numfaces;
3090
3091                 // make the model surface list (used by shadowing/lighting)
3092                 mod->surfacelist = Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->surfacelist));
3093                 for (j = 0;j < mod->nummodelsurfaces;j++)
3094                         mod->surfacelist[j] = mod->firstmodelsurface + j;
3095
3096                 // this gets altered below if sky is used
3097                 mod->DrawSky = NULL;
3098                 mod->Draw = R_Q1BSP_Draw;
3099                 mod->GetLightInfo = R_Q1BSP_GetLightInfo;
3100                 mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
3101                 mod->DrawLight = R_Q1BSP_DrawLight;
3102                 if (i != 0)
3103                 {
3104                         mod->brush.GetPVS = NULL;
3105                         mod->brush.FatPVS = NULL;
3106                         mod->brush.BoxTouchingPVS = NULL;
3107                         mod->brush.BoxTouchingLeafPVS = NULL;
3108                         mod->brush.BoxTouchingVisibleLeafs = NULL;
3109                         mod->brush.LightPoint = NULL;
3110                         mod->brush.AmbientSoundLevelsForPoint = NULL;
3111                 }
3112                 Mod_Q1BSP_BuildLightmapUpdateChains(loadmodel->mempool, mod);
3113                 if (mod->nummodelsurfaces)
3114                 {
3115                         // LordHavoc: calculate bmodel bounding box rather than trusting what it says
3116                         mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
3117                         mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
3118                         modelyawradius = 0;
3119                         modelradius = 0;
3120                         for (j = 0, surface = &mod->data_surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surface++)
3121                         {
3122                                 // we only need to have a drawsky function if it is used(usually only on world model)
3123                                 if (surface->texture->basematerialflags & MATERIALFLAG_SKY)
3124                                         mod->DrawSky = R_Q1BSP_DrawSky;
3125                                 // calculate bounding shapes
3126                                 for (k = 0, vec = (surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex);k < surface->num_vertices;k++, vec += 3)
3127                                 {
3128                                         if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
3129                                         if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
3130                                         if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
3131                                         if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
3132                                         if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
3133                                         if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
3134                                         dist = vec[0]*vec[0]+vec[1]*vec[1];
3135                                         if (modelyawradius < dist)
3136                                                 modelyawradius = dist;
3137                                         dist += vec[2]*vec[2];
3138                                         if (modelradius < dist)
3139                                                 modelradius = dist;
3140                                 }
3141                         }
3142                         modelyawradius = sqrt(modelyawradius);
3143                         modelradius = sqrt(modelradius);
3144                         mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
3145                         mod->yawmins[2] = mod->normalmins[2];
3146                         mod->yawmaxs[2] = mod->normalmaxs[2];
3147                         mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
3148                         mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
3149                         mod->radius = modelradius;
3150                         mod->radius2 = modelradius * modelradius;
3151                 }
3152                 else
3153                 {
3154                         // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
3155                         Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadmodel->name);
3156                 }
3157                 //mod->brushq1.num_visleafs = bm->visleafs;
3158         }
3159
3160         Mod_Q1BSP_LoadMapBrushes();
3161
3162         //Mod_Q1BSP_ProcessLightList();
3163
3164         if (developer.integer)
3165                 Con_Printf("Some stats for q1bsp model \"%s\": %i faces, %i nodes, %i leafs, %i visleafs, %i visleafportals\n", loadmodel->name, loadmodel->num_surfaces, loadmodel->brush.num_nodes, loadmodel->brush.num_leafs, mod->brush.num_pvsclusters, loadmodel->brush.num_portals);
3166 }
3167
3168 static void Mod_Q2BSP_LoadEntities(lump_t *l)
3169 {
3170 }
3171
3172 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
3173 {
3174 /*
3175         d_t *in;
3176         m_t *out;
3177         int i, count;
3178
3179         in = (void *)(mod_base + l->fileofs);
3180         if (l->filelen % sizeof(*in))
3181                 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3182         count = l->filelen / sizeof(*in);
3183         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3184
3185         loadmodel-> = out;
3186         loadmodel->num = count;
3187
3188         for (i = 0;i < count;i++, in++, out++)
3189         {
3190         }
3191 */
3192 }
3193
3194 static void Mod_Q2BSP_LoadVertices(lump_t *l)
3195 {
3196 /*
3197         d_t *in;
3198         m_t *out;
3199         int i, count;
3200
3201         in = (void *)(mod_base + l->fileofs);
3202         if (l->filelen % sizeof(*in))
3203                 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3204         count = l->filelen / sizeof(*in);
3205         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3206
3207         loadmodel-> = out;
3208         loadmodel->num = count;
3209
3210         for (i = 0;i < count;i++, in++, out++)
3211         {
3212         }
3213 */
3214 }
3215
3216 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
3217 {
3218 /*
3219         d_t *in;
3220         m_t *out;
3221         int i, count;
3222
3223         in = (void *)(mod_base + l->fileofs);
3224         if (l->filelen % sizeof(*in))
3225                 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
3226         count = l->filelen / sizeof(*in);
3227         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3228
3229         loadmodel-> = out;
3230         loadmodel->num = count;
3231
3232         for (i = 0;i < count;i++, in++, out++)
3233         {
3234         }
3235 */
3236 }
3237
3238 static void Mod_Q2BSP_LoadNodes(lump_t *l)
3239 {
3240 /*
3241         d_t *in;
3242         m_t *out;
3243         int i, count;
3244
3245         in = (void *)(mod_base + l->fileofs);
3246         if (l->filelen % sizeof(*in))
3247                 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3248         count = l->filelen / sizeof(*in);
3249         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3250
3251         loadmodel-> = out;
3252         loadmodel->num = count;
3253
3254         for (i = 0;i < count;i++, in++, out++)
3255         {
3256         }
3257 */
3258 }
3259
3260 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
3261 {
3262 /*
3263         d_t *in;
3264         m_t *out;
3265         int i, count;
3266
3267         in = (void *)(mod_base + l->fileofs);
3268         if (l->filelen % sizeof(*in))
3269                 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
3270         count = l->filelen / sizeof(*in);
3271         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3272
3273         loadmodel-> = out;
3274         loadmodel->num = count;
3275
3276         for (i = 0;i < count;i++, in++, out++)
3277         {
3278         }
3279 */
3280 }
3281
3282 static void Mod_Q2BSP_LoadFaces(lump_t *l)
3283 {
3284 /*
3285         d_t *in;
3286         m_t *out;
3287         int i, count;
3288
3289         in = (void *)(mod_base + l->fileofs);
3290         if (l->filelen % sizeof(*in))
3291                 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3292         count = l->filelen / sizeof(*in);
3293         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3294
3295         loadmodel-> = out;
3296         loadmodel->num = count;
3297
3298         for (i = 0;i < count;i++, in++, out++)
3299         {
3300         }
3301 */
3302 }
3303
3304 static void Mod_Q2BSP_LoadLighting(lump_t *l)
3305 {
3306 /*
3307         d_t *in;
3308         m_t *out;
3309         int i, count;
3310
3311         in = (void *)(mod_base + l->fileofs);
3312         if (l->filelen % sizeof(*in))
3313                 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
3314         count = l->filelen / sizeof(*in);
3315         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3316
3317         loadmodel-> = out;
3318         loadmodel->num = count;
3319
3320         for (i = 0;i < count;i++, in++, out++)
3321         {
3322         }
3323 */
3324 }
3325
3326 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
3327 {
3328 /*
3329         d_t *in;
3330         m_t *out;
3331         int i, count;
3332
3333         in = (void *)(mod_base + l->fileofs);
3334         if (l->filelen % sizeof(*in))
3335                 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3336         count = l->filelen / sizeof(*in);
3337         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3338
3339         loadmodel-> = out;
3340         loadmodel->num = count;
3341
3342         for (i = 0;i < count;i++, in++, out++)
3343         {
3344         }
3345 */
3346 }
3347
3348 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3349 {
3350 /*
3351         d_t *in;
3352         m_t *out;
3353         int i, count;
3354
3355         in = (void *)(mod_base + l->fileofs);
3356         if (l->filelen % sizeof(*in))
3357                 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3358         count = l->filelen / sizeof(*in);
3359         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3360
3361         loadmodel-> = out;
3362         loadmodel->num = count;
3363
3364         for (i = 0;i < count;i++, in++, out++)
3365         {
3366         }
3367 */
3368 }
3369
3370 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3371 {
3372 /*
3373         d_t *in;
3374         m_t *out;
3375         int i, count;
3376
3377         in = (void *)(mod_base + l->fileofs);
3378         if (l->filelen % sizeof(*in))
3379                 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3380         count = l->filelen / sizeof(*in);
3381         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3382
3383         loadmodel-> = out;
3384         loadmodel->num = count;
3385
3386         for (i = 0;i < count;i++, in++, out++)
3387         {
3388         }
3389 */
3390 }
3391
3392 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3393 {
3394 /*
3395         d_t *in;
3396         m_t *out;
3397         int i, count;
3398
3399         in = (void *)(mod_base + l->fileofs);
3400         if (l->filelen % sizeof(*in))
3401                 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3402         count = l->filelen / sizeof(*in);
3403         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3404
3405         loadmodel-> = out;
3406         loadmodel->num = count;
3407
3408         for (i = 0;i < count;i++, in++, out++)
3409         {
3410         }
3411 */
3412 }
3413
3414 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3415 {
3416 /*
3417         d_t *in;
3418         m_t *out;
3419         int i, count;
3420
3421         in = (void *)(mod_base + l->fileofs);
3422         if (l->filelen % sizeof(*in))
3423                 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3424         count = l->filelen / sizeof(*in);
3425         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3426
3427         loadmodel-> = out;
3428         loadmodel->num = count;
3429
3430         for (i = 0;i < count;i++, in++, out++)
3431         {
3432         }
3433 */
3434 }
3435
3436 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3437 {
3438 /*
3439         d_t *in;
3440         m_t *out;
3441         int i, count;
3442
3443         in = (void *)(mod_base + l->fileofs);
3444         if (l->filelen % sizeof(*in))
3445                 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3446         count = l->filelen / sizeof(*in);
3447         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3448
3449         loadmodel-> = out;
3450         loadmodel->num = count;
3451
3452         for (i = 0;i < count;i++, in++, out++)
3453         {
3454         }
3455 */
3456 }
3457
3458 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3459 {
3460 /*
3461         d_t *in;
3462         m_t *out;
3463         int i, count;
3464
3465         in = (void *)(mod_base + l->fileofs);
3466         if (l->filelen % sizeof(*in))
3467                 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3468         count = l->filelen / sizeof(*in);
3469         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3470
3471         loadmodel-> = out;
3472         loadmodel->num = count;
3473
3474         for (i = 0;i < count;i++, in++, out++)
3475         {
3476         }
3477 */
3478 }
3479
3480 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3481 {
3482 /*
3483         d_t *in;
3484         m_t *out;
3485         int i, count;
3486
3487         in = (void *)(mod_base + l->fileofs);
3488         if (l->filelen % sizeof(*in))
3489                 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3490         count = l->filelen / sizeof(*in);
3491         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3492
3493         loadmodel-> = out;
3494         loadmodel->num = count;
3495
3496         for (i = 0;i < count;i++, in++, out++)
3497         {
3498         }
3499 */
3500 }
3501
3502 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3503 {
3504 /*
3505         d_t *in;
3506         m_t *out;
3507         int i, count;
3508
3509         in = (void *)(mod_base + l->fileofs);
3510         if (l->filelen % sizeof(*in))
3511                 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3512         count = l->filelen / sizeof(*in);
3513         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3514
3515         loadmodel-> = out;
3516         loadmodel->num = count;
3517
3518         for (i = 0;i < count;i++, in++, out++)
3519         {
3520         }
3521 */
3522 }
3523
3524 static void Mod_Q2BSP_LoadModels(lump_t *l)
3525 {
3526 /*
3527         d_t *in;
3528         m_t *out;
3529         int i, count;
3530
3531         in = (void *)(mod_base + l->fileofs);
3532         if (l->filelen % sizeof(*in))
3533                 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3534         count = l->filelen / sizeof(*in);
3535         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3536
3537         loadmodel-> = out;
3538         loadmodel->num = count;
3539
3540         for (i = 0;i < count;i++, in++, out++)
3541         {
3542         }
3543 */
3544 }
3545
3546 void static Mod_Q2BSP_Load(model_t *mod, void *buffer, void *bufferend)
3547 {
3548         int i;
3549         q2dheader_t *header;
3550
3551         Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3552
3553         mod->type = mod_brushq2;
3554
3555         header = (q2dheader_t *)buffer;
3556
3557         i = LittleLong(header->version);
3558         if (i != Q2BSPVERSION)
3559                 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3560         mod->brush.ishlbsp = false;
3561         if (loadmodel->isworldmodel)
3562                 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3563
3564         mod_base = (qbyte *)header;
3565
3566         // swap all the lumps
3567         for (i = 0;i < (int) sizeof(*header) / 4;i++)
3568                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3569
3570         // store which lightmap format to use
3571         mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3572
3573         Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3574         Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3575         Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3576         Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3577         Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3578         Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3579         Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3580         Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3581         Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3582         Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3583         Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3584         Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3585         Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3586         Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3587         Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3588         Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3589         Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3590         // LordHavoc: must go last because this makes the submodels
3591         Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3592 }
3593
3594 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents);
3595 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents);
3596
3597 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3598 {
3599         const char *data;
3600         char key[128], value[4096];
3601         float v[3];
3602         loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3603         loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3604         loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3605         if (!l->filelen)
3606                 return;
3607         loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3608         memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3609         data = loadmodel->brush.entities;
3610         // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3611         if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3612         {
3613                 while (1)
3614                 {
3615                         if (!COM_ParseToken(&data, false))
3616                                 break; // error
3617                         if (com_token[0] == '}')
3618                                 break; // end of worldspawn
3619                         if (com_token[0] == '_')
3620                                 strcpy(key, com_token + 1);
3621                         else
3622                                 strcpy(key, com_token);
3623                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
3624                                 key[strlen(key)-1] = 0;
3625                         if (!COM_ParseToken(&data, false))
3626                                 break; // error
3627                         strcpy(value, com_token);
3628                         if (!strcmp("gridsize", key))
3629                         {
3630                                 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3631                                         VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3632                         }
3633                 }
3634         }
3635 }
3636
3637 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3638 {
3639         q3dtexture_t *in;
3640         texture_t *out;
3641         int i, count;
3642         int j, c;
3643         fssearch_t *search;
3644         char *f;
3645         const char *text;
3646         int flags, flags2, numparameters, passnumber;
3647         char shadername[Q3PATHLENGTH];
3648         char sky[Q3PATHLENGTH];
3649         char firstpasstexturename[Q3PATHLENGTH];
3650         char parameter[4][Q3PATHLENGTH];
3651
3652         in = (void *)(mod_base + l->fileofs);
3653         if (l->filelen % sizeof(*in))
3654                 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3655         count = l->filelen / sizeof(*in);
3656         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3657
3658         loadmodel->data_textures = out;
3659         loadmodel->num_textures = count;
3660
3661         for (i = 0;i < count;i++, in++, out++)
3662         {
3663                 strlcpy (out->name, in->name, sizeof (out->name));
3664                 out->surfaceflags = LittleLong(in->surfaceflags);
3665                 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, LittleLong(in->contents));
3666                 out->surfaceparms = -1;
3667         }
3668
3669         // do a quick parse of shader files to get surfaceparms
3670         if ((search = FS_Search("scripts/*.shader", true, false)))
3671         {
3672                 for (i = 0;i < search->numfilenames;i++)
3673                 {
3674                         if ((f = FS_LoadFile(search->filenames[i], tempmempool, false)))
3675                         {
3676                                 text = f;
3677                                 while (COM_ParseToken(&text, false))
3678                                 {
3679                                         strlcpy (shadername, com_token, sizeof (shadername));
3680                                         flags = 0;
3681                                         flags2 = 0;
3682                                         sky[0] = 0;
3683                                         passnumber = 0;
3684                                         firstpasstexturename[0] = 0;
3685                                         if (COM_ParseToken(&text, false) && !strcasecmp(com_token, "{"))
3686                                         {
3687                                                 while (COM_ParseToken(&text, false))
3688                                                 {
3689                                                         if (!strcasecmp(com_token, "}"))
3690                                                                 break;
3691                                                         else if (!strcasecmp(com_token, "{"))
3692                                                         {
3693                                                                 while (COM_ParseToken(&text, false))
3694                                                                 {
3695                                                                         if (!strcasecmp(com_token, "}"))
3696                                                                                 break;
3697                                                                         if (!strcasecmp(com_token, "\n"))
3698                                                                                 continue;
3699                                                                         numparameters = 0;
3700                                                                         for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
3701                                                                         {
3702                                                                                 if (j < 4)
3703                                                                                 {
3704                                                                                         strlcpy(parameter[j], com_token, sizeof(parameter[j]));
3705                                                                                         numparameters = j + 1;
3706                                                                                 }
3707                                                                                 if (!COM_ParseToken(&text, true))
3708                                                                                         break;
3709                                                                         }
3710                                                                         if (developer.integer >= 2)
3711                                                                         {
3712                                                                                 Con_Printf("%s %i: ", shadername, passnumber);
3713                                                                                 for (j = 0;j < numparameters;j++)
3714                                                                                         Con_Printf(" %s", parameter[j]);
3715                                                                                 Con_Print("\n");
3716                                                                         }
3717                                                                         if (passnumber == 0 && numparameters >= 1)
3718                                                                         {
3719                                                                                 if (!strcasecmp(parameter[0], "blendfunc") && (flags & Q3SURFACEPARM_TRANS))
3720                                                                                 {
3721                                                                                         if (numparameters == 2 && !strcasecmp(parameter[1], "add"))
3722                                                                                                 flags2 |= Q3TEXTUREFLAG_ADDITIVE;
3723                                                                                         else if (numparameters == 3 && !strcasecmp(parameter[1], "gl_one") && !strcasecmp(parameter[2], "gl_one"))
3724                                                                                                 flags2 |= Q3TEXTUREFLAG_ADDITIVE;
3725                                                                                         else if (numparameters == 3 && !strcasecmp(parameter[1], "gl_src_alpha") && !strcasecmp(parameter[2], "gl_one"))
3726                                                                                                 flags2 |= Q3TEXTUREFLAG_ADDITIVE;
3727                                                                                 }
3728                                                                                 else if (numparameters >= 2 && (!strcasecmp(parameter[0], "map") || !strcasecmp(parameter[0], "clampmap")))
3729                                                                                         strlcpy(firstpasstexturename, parameter[1], sizeof(firstpasstexturename));
3730                                                                                 else if (numparameters >= 3 && !strcasecmp(parameter[0], "animmap"))
3731                                                                                         strlcpy(firstpasstexturename, parameter[2], sizeof(firstpasstexturename));
3732                                                                                 else if (numparameters >= 2 && !strcasecmp(parameter[0], "alphafunc"))
3733                                                                                         flags2 |= Q3TEXTUREFLAG_ALPHATEST;
3734                                                                         }
3735                                                                         // break out a level if it was }
3736                                                                         if (!strcasecmp(com_token, "}"))
3737                                                                                 break;
3738                                                                 }
3739                                                                 passnumber++;
3740                                                                 continue;
3741                                                         }
3742                                                         numparameters = 0;
3743                                                         for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
3744                                                         {
3745                                                                 if (j < 4)
3746                                                                 {
3747                                                                         strlcpy(parameter[j], com_token, sizeof(parameter[j]));
3748                                                                         numparameters = j + 1;
3749                                                                 }
3750                                                                 if (!COM_ParseToken(&text, true))
3751                                                                         break;
3752                                                         }
3753                                                         if (i == 0 && !strcasecmp(com_token, "}"))
3754                                                                 break;
3755                                                         if (developer.integer >= 2)
3756                                                         {
3757                                                                 Con_Printf("%s: ", shadername);
3758                                                                 for (j = 0;j < numparameters;j++)
3759                                                                         Con_Printf(" %s", parameter[j]);
3760                                                                 Con_Print("\n");
3761                                                         }
3762                                                         if (numparameters < 1)
3763                                                                 continue;
3764                                                         if (!strcasecmp(parameter[0], "surfaceparm") && numparameters >= 2)
3765                                                         {
3766                                                                 if (!strcasecmp(parameter[1], "alphashadow"))
3767                                                                         flags |= Q3SURFACEPARM_ALPHASHADOW;
3768                                                                 else if (!strcasecmp(parameter[1], "areaportal"))
3769                                                                         flags |= Q3SURFACEPARM_AREAPORTAL;
3770                                                                 else if (!strcasecmp(parameter[1], "clusterportal"))
3771                                                                         flags |= Q3SURFACEPARM_CLUSTERPORTAL;
3772                                                                 else if (!strcasecmp(parameter[1], "detail"))
3773                                                                         flags |= Q3SURFACEPARM_DETAIL;
3774                                                                 else if (!strcasecmp(parameter[1], "donotenter"))
3775                                                                         flags |= Q3SURFACEPARM_DONOTENTER;
3776                                                                 else if (!strcasecmp(parameter[1], "fog"))
3777                                                                         flags |= Q3SURFACEPARM_FOG;
3778                                                                 else if (!strcasecmp(parameter[1], "lava"))
3779                                                                         flags |= Q3SURFACEPARM_LAVA;
3780                                                                 else if (!strcasecmp(parameter[1], "lightfilter"))
3781                                                                         flags |= Q3SURFACEPARM_LIGHTFILTER;
3782                                                                 else if (!strcasecmp(parameter[1], "metalsteps"))
3783                                                                         flags |= Q3SURFACEPARM_METALSTEPS;
3784                                                                 else if (!strcasecmp(parameter[1], "nodamage"))
3785                                                                         flags |= Q3SURFACEPARM_NODAMAGE;
3786                                                                 else if (!strcasecmp(parameter[1], "nodlight"))
3787                                                                         flags |= Q3SURFACEPARM_NODLIGHT;
3788                                                                 else if (!strcasecmp(parameter[1], "nodraw"))
3789                                                                         flags |= Q3SURFACEPARM_NODRAW;
3790                                                                 else if (!strcasecmp(parameter[1], "nodrop"))
3791                                                                         flags |= Q3SURFACEPARM_NODROP;
3792                                                                 else if (!strcasecmp(parameter[1], "noimpact"))
3793                                                                         flags |= Q3SURFACEPARM_NOIMPACT;
3794                                                                 else if (!strcasecmp(parameter[1], "nolightmap"))
3795                                                                         flags |= Q3SURFACEPARM_NOLIGHTMAP;
3796                                                                 else if (!strcasecmp(parameter[1], "nomarks"))
3797                                                                         flags |= Q3SURFACEPARM_NOMARKS;
3798                                                                 else if (!strcasecmp(parameter[1], "nomipmaps"))
3799                                                                         flags |= Q3SURFACEPARM_NOMIPMAPS;
3800                                                                 else if (!strcasecmp(parameter[1], "nonsolid"))
3801                                                                         flags |= Q3SURFACEPARM_NONSOLID;
3802                                                                 else if (!strcasecmp(parameter[1], "origin"))
3803                                                                         flags |= Q3SURFACEPARM_ORIGIN;
3804                                                                 else if (!strcasecmp(parameter[1], "playerclip"))
3805                                                                         flags |= Q3SURFACEPARM_PLAYERCLIP;
3806                                                                 else if (!strcasecmp(parameter[1], "sky"))
3807                                                                         flags |= Q3SURFACEPARM_SKY;
3808                                                                 else if (!strcasecmp(parameter[1], "slick"))
3809                                                                         flags |= Q3SURFACEPARM_SLICK;
3810                                                                 else if (!strcasecmp(parameter[1], "slime"))
3811                                                                         flags |= Q3SURFACEPARM_SLIME;
3812                                                                 else if (!strcasecmp(parameter[1], "structural"))
3813                                                                         flags |= Q3SURFACEPARM_STRUCTURAL;
3814                                                                 else if (!strcasecmp(parameter[1], "trans"))
3815                                                                         flags |= Q3SURFACEPARM_TRANS;
3816                                                                 else if (!strcasecmp(parameter[1], "water"))
3817                                                                         flags |= Q3SURFACEPARM_WATER;
3818                                                                 else if (!strcasecmp(parameter[1], "pointlight"))
3819                                                                         flags |= Q3SURFACEPARM_POINTLIGHT;
3820                                                                 else
3821                                                                         Con_Printf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[i], parameter[1]);
3822                                                         }
3823                                                         else if (!strcasecmp(parameter[0], "sky") && numparameters >= 2)
3824                                                                 strlcpy(sky, parameter[1], sizeof(sky));
3825                                                         else if (!strcasecmp(parameter[0], "skyparms") && numparameters >= 2)
3826                                                         {
3827                                                                 if (!atoi(parameter[1]) && strcasecmp(parameter[1], "-"))
3828                                                                         strlcpy(sky, parameter[1], sizeof(sky));
3829                                                         }
3830                                                         else if (!strcasecmp(parameter[0], "cull") && numparameters >= 2)
3831                                                         {
3832                                                                 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "twosided"))
3833                                                                         flags2 |= Q3TEXTUREFLAG_TWOSIDED;
3834                                                         }
3835                                                         else if (!strcasecmp(parameter[0], "nomipmaps"))
3836                                                                 flags2 |= Q3TEXTUREFLAG_NOMIPMAPS;
3837                                                         else if (!strcasecmp(parameter[0], "nopicmip"))
3838                                                                 flags2 |= Q3TEXTUREFLAG_NOPICMIP;
3839                                                         else if (!strcasecmp(parameter[0], "deformvertexes") && numparameters >= 2)
3840                                                         {
3841                                                                 if (!strcasecmp(parameter[1], "autosprite") && numparameters == 2)
3842                                                                         flags2 |= Q3TEXTUREFLAG_AUTOSPRITE;
3843                                                                 if (!strcasecmp(parameter[1], "autosprite2") && numparameters == 2)
3844                                                                         flags2 |= Q3TEXTUREFLAG_AUTOSPRITE2;
3845                                                         }
3846                                                 }
3847                                                 // add shader to list (shadername and flags)
3848                                                 // actually here we just poke into the texture settings
3849                                                 for (j = 0, out = loadmodel->data_textures;j < loadmodel->num_textures;j++, out++)
3850                                                 {
3851                                                         if (!strcasecmp(out->name, shadername))
3852                                                         {
3853                                                                 out->surfaceparms = flags;
3854                                                                 out->textureflags = flags2;
3855                                                                 out->basematerialflags = 0;
3856                                                                 if (out->surfaceparms & Q3SURFACEPARM_NODRAW)
3857                                                                         out->basematerialflags |= MATERIALFLAG_NODRAW;
3858                                                                 else if (out->surfaceparms & Q3SURFACEPARM_SKY)
3859                                                                         out->basematerialflags |= MATERIALFLAG_SKY;
3860                                                                 else if (out->surfaceparms & Q3SURFACEPARM_LAVA)
3861                                                                         out->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_FULLBRIGHT;
3862                                                                 else if (out->surfaceparms & Q3SURFACEPARM_SLIME)
3863                                                                         out->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_WATERALPHA;
3864                                                                 else if (out->surfaceparms & Q3SURFACEPARM_WATER)
3865                                                                         out->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_WATERALPHA;
3866                                                                 else
3867                                                                         out->basematerialflags |= MATERIALFLAG_WALL;
3868                                                                 if (out->textureflags & Q3TEXTUREFLAG_ALPHATEST)
3869                                                                 {
3870                                                                         // FIXME: support alpha test?
3871                                                                         out->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_TRANSPARENT;
3872                                                                 }
3873                                                                 else if (out->surfaceparms & Q3SURFACEPARM_TRANS)
3874                                                                 {
3875                                                                         if (out->textureflags & Q3TEXTUREFLAG_ADDITIVE)
3876                                                                                 out->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_TRANSPARENT;
3877                                                                         else
3878                                                                                 out->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_TRANSPARENT;
3879                                                                 }
3880                                                                 strlcpy(out->firstpasstexturename, firstpasstexturename, sizeof(out->firstpasstexturename));
3881                                                                 if ((flags & Q3SURFACEPARM_SKY) && sky[0])
3882                                                                 {
3883                                                                         // quake3 seems to append a _ to the skybox name, so this must do so as well
3884                                                                         dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", sky);
3885                                                                 }
3886                                                         }
3887                                                 }
3888                                         }
3889                                         else
3890                                         {
3891                                                 Con_Printf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[i], com_token);
3892                                                 goto parseerror;
3893                                         }
3894                                 }
3895 parseerror:
3896                                 Mem_Free(f);
3897                         }
3898                 }
3899         }
3900
3901         c = 0;
3902         for (j = 0, out = loadmodel->data_textures;j < loadmodel->num_textures;j++, out++)
3903         {
3904                 if (out->surfaceparms == -1)
3905                 {
3906                         c++;
3907                         Con_DPrintf("%s: No shader found for texture \"%s\"\n", loadmodel->name, out->name);
3908                         out->surfaceparms = 0;
3909                         if (out->surfaceflags & Q3SURFACEFLAG_NODRAW)
3910                                 out->basematerialflags |= MATERIALFLAG_NODRAW;
3911                         else if (out->surfaceflags & Q3SURFACEFLAG_SKY)
3912                                 out->basematerialflags |= MATERIALFLAG_SKY;
3913                         else
3914                                 out->basematerialflags |= MATERIALFLAG_WALL;
3915                         // these are defaults
3916                         //if (!strncmp(out->name, "textures/skies/", 15))
3917                         //      out->surfaceparms |= Q3SURFACEPARM_SKY;
3918                         //if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk")
3919                         // || !strcmp(out->name, "nodraw") || !strcmp(out->name, "common/nodraw") || !strcmp(out->name, "textures/common/nodraw"))
3920                         //      out->surfaceparms |= Q3SURFACEPARM_NODRAW;
3921                         //if (R_TextureHasAlpha(out->skin.base))
3922                         //      out->surfaceparms |= Q3SURFACEPARM_TRANS;
3923                 }
3924                 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))
3925                         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))
3926                                 if (cls.state != ca_dedicated)
3927                                         Con_Printf("%s: texture loading for shader \"%s\" failed (first layer \"%s\" not found either)\n", loadmodel->name, out->name, out->firstpasstexturename);
3928                 // no animation
3929                 out->currentframe = out;
3930         }
3931         if (c)
3932                 Con_DPrintf("%s: %i textures missing shaders\n", loadmodel->name, c);
3933 }
3934
3935 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3936 {
3937         q3dplane_t *in;
3938         mplane_t *out;
3939         int i, count;
3940
3941         in = (void *)(mod_base + l->fileofs);
3942         if (l->filelen % sizeof(*in))
3943                 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3944         count = l->filelen / sizeof(*in);
3945         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3946
3947         loadmodel->brush.data_planes = out;
3948         loadmodel->brush.num_planes = count;
3949
3950         for (i = 0;i < count;i++, in++, out++)
3951         {
3952                 out->normal[0] = LittleFloat(in->normal[0]);
3953                 out->normal[1] = LittleFloat(in->normal[1]);
3954                 out->normal[2] = LittleFloat(in->normal[2]);
3955                 out->dist = LittleFloat(in->dist);
3956                 PlaneClassify(out);
3957         }
3958 }
3959
3960 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3961 {
3962         q3dbrushside_t *in;
3963         q3mbrushside_t *out;
3964         int i, n, count;
3965
3966         in = (void *)(mod_base + l->fileofs);
3967         if (l->filelen % sizeof(*in))
3968                 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3969         count = l->filelen / sizeof(*in);
3970         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3971
3972         loadmodel->brush.data_brushsides = out;
3973         loadmodel->brush.num_brushsides = count;
3974
3975         for (i = 0;i < count;i++, in++, out++)
3976         {
3977                 n = LittleLong(in->planeindex);
3978                 if (n < 0 || n >= loadmodel->brush.num_planes)
3979                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brush.num_planes);
3980                 out->plane = loadmodel->brush.data_planes + n;
3981                 n = LittleLong(in->textureindex);
3982                 if (n < 0 || n >= loadmodel->num_textures)
3983                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->num_textures);
3984                 out->texture = loadmodel->data_textures + n;
3985         }
3986 }
3987
3988 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3989 {
3990         q3dbrush_t *in;
3991         q3mbrush_t *out;
3992         int i, j, n, c, count, maxplanes;
3993         mplane_t *planes;
3994
3995         in = (void *)(mod_base + l->fileofs);
3996         if (l->filelen % sizeof(*in))
3997                 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3998         count = l->filelen / sizeof(*in);
3999         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4000
4001         loadmodel->brush.data_brushes = out;
4002         loadmodel->brush.num_brushes = count;
4003
4004         maxplanes = 0;
4005         planes = NULL;
4006
4007         for (i = 0;i < count;i++, in++, out++)
4008         {
4009                 n = LittleLong(in->firstbrushside);
4010                 c = LittleLong(in->numbrushsides);
4011                 if (n < 0 || n + c > loadmodel->brush.num_brushsides)
4012                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brush.num_brushsides);
4013                 out->firstbrushside = loadmodel->brush.data_brushsides + n;
4014                 out->numbrushsides = c;
4015                 n = LittleLong(in->textureindex);
4016                 if (n < 0 || n >= loadmodel->num_textures)
4017                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->num_textures);
4018                 out->texture = loadmodel->data_textures + n;
4019
4020                 // make a list of mplane_t structs to construct a colbrush from
4021                 if (maxplanes < out->numbrushsides)
4022                 {
4023                         maxplanes = out->numbrushsides;
4024                         if (planes)
4025                                 Mem_Free(planes);
4026                         planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
4027                 }
4028                 for (j = 0;j < out->numbrushsides;j++)
4029                 {
4030                         VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
4031                         planes[j].dist = out->firstbrushside[j].plane->dist;
4032                 }
4033                 // make the colbrush from the planes
4034                 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents);
4035         }
4036         if (planes)
4037                 Mem_Free(planes);
4038 }
4039
4040 static void Mod_Q3BSP_LoadEffects(lump_t *l)
4041 {
4042         q3deffect_t *in;
4043         q3deffect_t *out;
4044         int i, n, count;
4045
4046         in = (void *)(mod_base + l->fileofs);
4047         if (l->filelen % sizeof(*in))
4048                 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
4049         count = l->filelen / sizeof(*in);
4050         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4051
4052         loadmodel->brushq3.data_effects = out;
4053         loadmodel->brushq3.num_effects = count;
4054
4055         for (i = 0;i < count;i++, in++, out++)
4056         {
4057                 strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
4058                 n = LittleLong(in->brushindex);
4059                 if (n < 0 || n >= loadmodel->brush.num_brushes)
4060                         Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brush.num_brushes);
4061                 out->brushindex = n;
4062                 out->unknown = LittleLong(in->unknown);
4063         }
4064 }
4065
4066 static void Mod_Q3BSP_LoadVertices(lump_t *l)
4067 {
4068         q3dvertex_t *in;
4069         int i, count;
4070
4071         in = (void *)(mod_base + l->fileofs);
4072         if (l->filelen % sizeof(*in))
4073                 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
4074         loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
4075         loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * (sizeof(float) * (3 + 2 + 2 + 4)));
4076         loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + count * 3;
4077         loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + count * 2;
4078         loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_texcoordlightmap2f + count * 2;
4079
4080         for (i = 0;i < count;i++, in++)
4081         {
4082                 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
4083                 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
4084                 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
4085                 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
4086                 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
4087                 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
4088                 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
4089                 // svector/tvector are calculated later in face loading
4090                 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
4091                 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
4092                 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
4093                 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
4094         }
4095 }
4096
4097 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
4098 {
4099         int *in;
4100         int *out;
4101         int i, count;
4102
4103         in = (void *)(mod_base + l->fileofs);
4104         if (l->filelen % sizeof(int[3]))
4105                 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
4106         count = l->filelen / sizeof(*in);
4107         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4108
4109         loadmodel->brushq3.num_triangles = count / 3;
4110         loadmodel->brushq3.data_element3i = out;
4111
4112         for (i = 0;i < count;i++, in++, out++)
4113         {
4114                 *out = LittleLong(*in);
4115                 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
4116                 {
4117                         Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
4118                         *out = 0;
4119                 }
4120         }
4121 }
4122
4123 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
4124 {
4125         q3dlightmap_t *in;
4126         rtexture_t **out;
4127         int i, count;
4128
4129         if (!l->filelen)
4130                 return;
4131         in = (void *)(mod_base + l->fileofs);
4132         if (l->filelen % sizeof(*in))
4133                 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
4134         count = l->filelen / sizeof(*in);
4135         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4136
4137         loadmodel->brushq3.data_lightmaps = out;
4138         loadmodel->brushq3.num_lightmaps = count;
4139
4140         for (i = 0;i < count;i++, in++, out++)
4141                 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
4142 }
4143
4144 static void Mod_Q3BSP_LoadFaces(lump_t *l)
4145 {
4146         q3dface_t *in, *oldin;
4147         msurface_t *out, *oldout;
4148         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;
4149         //int *originalelement3i;
4150         //int *originalneighbor3i;
4151         float *originalvertex3f;
4152         //float *originalsvector3f;
4153         //float *originaltvector3f;
4154         //float *originalnormal3f;
4155         float *originalcolor4f;
4156         float *originaltexcoordtexture2f;
4157         float *originaltexcoordlightmap2f;
4158         float *v;
4159         surfmesh_t *mesh, *tempmeshlist[1024];
4160
4161         in = (void *)(mod_base + l->fileofs);
4162         if (l->filelen % sizeof(*in))
4163                 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
4164         count = l->filelen / sizeof(*in);
4165         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4166
4167         loadmodel->data_surfaces = out;
4168         loadmodel->num_surfaces = count;
4169
4170         i = 0;
4171         for (meshnum = 0;i < count;meshnum++)
4172         {
4173                 oldi = i;
4174                 oldin = in;
4175                 oldout = out;
4176                 meshvertices = 0;
4177                 meshtriangles = 0;
4178                 for (;i < count;i++, in++, out++)
4179                 {
4180                         // check face type first
4181                         type = LittleLong(in->type);
4182                         if (type != Q3FACETYPE_POLYGON
4183                          && type != Q3FACETYPE_PATCH
4184                          && type != Q3FACETYPE_MESH
4185                          && type != Q3FACETYPE_FLARE)
4186                         {
4187                                 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, type);
4188                                 continue;
4189                         }
4190
4191                         n = LittleLong(in->textureindex);
4192                         if (n < 0 || n >= loadmodel->num_textures)
4193                         {
4194                                 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->num_textures);
4195                                 continue;
4196                         }
4197                         out->texture = loadmodel->data_textures + n;
4198                         n = LittleLong(in->effectindex);
4199                         if (n < -1 || n >= loadmodel->brushq3.num_effects)
4200                         {
4201                                 if (developer.integer >= 2)
4202                                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
4203                                 n = -1;
4204                         }
4205                         if (n == -1)
4206                                 out->effect = NULL;
4207                         else
4208                                 out->effect = loadmodel->brushq3.data_effects + n;
4209                         n = LittleLong(in->lightmapindex);
4210                         if (n >= loadmodel->brushq3.num_lightmaps)
4211                         {
4212                                 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
4213                                 n = -1;
4214                         }
4215                         else if (n < 0)
4216                                 n = -1;
4217                         if (n == -1)
4218                                 out->lightmaptexture = NULL;
4219                         else
4220                                 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
4221
4222                         firstvertex = LittleLong(in->firstvertex);
4223                         numvertices = LittleLong(in->numvertices);
4224                         firstelement = LittleLong(in->firstelement);
4225                         numtriangles = LittleLong(in->numelements) / 3;
4226                         if (numtriangles * 3 != LittleLong(in->numelements))
4227                         {
4228                                 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));
4229                                 continue;
4230                         }
4231                         if (firstvertex < 0 || firstvertex + numvertices > loadmodel->brushq3.num_vertices)
4232                         {
4233                                 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);
4234                                 continue;
4235                         }
4236                         if (firstelement < 0 || firstelement + numtriangles * 3 > loadmodel->brushq3.num_triangles * 3)
4237                         {
4238                                 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);
4239                                 continue;
4240                         }
4241                         switch(type)
4242                         {
4243                         case Q3FACETYPE_POLYGON:
4244                         case Q3FACETYPE_MESH:
4245                                 // no processing necessary
4246                                 break;
4247                         case Q3FACETYPE_PATCH:
4248                                 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
4249                                 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
4250                                 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))
4251                                 {
4252                                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
4253                                         continue;
4254                                 }
4255                                 originalvertex3f = loadmodel->brushq3.data_vertex3f + firstvertex * 3;
4256                                 // convert patch to Q3FACETYPE_MESH
4257                                 xtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
4258                                 ytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
4259                                 // bound to user settings
4260                                 xtess = bound(r_subdivisions_mintess.integer, xtess, r_subdivisions_maxtess.integer);
4261                                 ytess = bound(r_subdivisions_mintess.integer, ytess, r_subdivisions_maxtess.integer);
4262                                 // bound to sanity settings
4263                                 xtess = bound(1, xtess, 1024);
4264                                 ytess = bound(1, ytess, 1024);
4265                                 // bound to user limit on vertices
4266                                 while ((xtess > 1 || ytess > 1) && (((patchsize[0] - 1) * xtess) + 1) * (((patchsize[1] - 1) * ytess) + 1) > min(r_subdivisions_maxvertices.integer, 262144))
4267                                 {
4268                                         if (xtess > ytess)
4269                                                 xtess--;
4270                                         else
4271                                                 ytess--;
4272                                 }
4273                                 finalwidth = ((patchsize[0] - 1) * xtess) + 1;
4274                                 finalheight = ((patchsize[1] - 1) * ytess) + 1;
4275                                 numvertices = finalwidth * finalheight;
4276                                 numtriangles = (finalwidth - 1) * (finalheight - 1) * 2;
4277                                 break;
4278                         case Q3FACETYPE_FLARE:
4279                                 if (developer.integer >= 2)
4280                                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
4281                                 // don't render it
4282                                 continue;
4283                         }
4284                         out->num_vertices = numvertices;
4285                         out->num_triangles = numtriangles;
4286                         if (meshvertices + out->num_vertices > 65536)
4287                                 break;
4288                         meshvertices += out->num_vertices;
4289                         meshtriangles += out->num_triangles;
4290                 }
4291
4292                 i = oldi;
4293                 in = oldin;
4294                 out = oldout;
4295                 mesh = tempmeshlist[meshnum] = Mod_AllocSurfMesh(loadmodel->mempool, meshvertices, meshtriangles, false, false, true, false);
4296                 meshvertices = 0;
4297                 meshtriangles = 0;
4298                 for (;i < count && meshvertices + out->num_vertices <= mesh->num_vertices;i++, in++, out++)
4299                 {
4300                         if (out->num_vertices < 3 || out->num_triangles < 1)
4301                                 continue;
4302
4303                         type = LittleLong(in->type);
4304                         firstvertex = LittleLong(in->firstvertex);
4305                         firstelement = LittleLong(in->firstelement);
4306                         out->groupmesh = mesh;
4307                         out->num_firstvertex = meshvertices;
4308                         out->num_firsttriangle = meshtriangles;
4309                         switch(type)
4310                         {
4311                         case Q3FACETYPE_POLYGON:
4312                         case Q3FACETYPE_MESH:
4313                                 // no processing necessary
4314                                 for (j = 0;j < out->num_vertices;j++)
4315                                 {
4316                                         (out->groupmesh->data_vertex3f + 3 * out->num_firstvertex)[j * 3 + 0] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 0];
4317                                         (out->groupmesh->data_vertex3f + 3 * out->num_firstvertex)[j * 3 + 1] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 1];
4318                                         (out->groupmesh->data_vertex3f + 3 * out->num_firstvertex)[j * 3 + 2] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 2];
4319                                         (out->groupmesh->data_texcoordtexture2f + 2 * out->num_firstvertex)[j * 2 + 0] = loadmodel->brushq3.data_texcoordtexture2f[(firstvertex + j) * 2 + 0];
4320                                         (out->groupmesh->data_texcoordtexture2f + 2 * out->num_firstvertex)[j * 2 + 1] = loadmodel->brushq3.data_texcoordtexture2f[(firstvertex + j) * 2 + 1];
4321                                         (out->groupmesh->data_texcoordlightmap2f + 2 * out->num_firstvertex)[j * 2 + 0] = loadmodel->brushq3.data_texcoordlightmap2f[(firstvertex + j) * 2 + 0];
4322                                         (out->groupmesh->data_texcoordlightmap2f + 2 * out->num_firstvertex)[j * 2 + 1] = loadmodel->brushq3.data_texcoordlightmap2f[(firstvertex + j) * 2 + 1];
4323                                         (out->groupmesh->data_lightmapcolor4f + 4 * out->num_firstvertex)[j * 4 + 0] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 0];
4324                                         (out->groupmesh->data_lightmapcolor4f + 4 * out->num_firstvertex)[j * 4 + 1] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 1];
4325                                         (out->groupmesh->data_lightmapcolor4f + 4 * out->num_firstvertex)[j * 4 + 2] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 2];
4326                                         (out->groupmesh->data_lightmapcolor4f + 4 * out->num_firstvertex)[j * 4 + 3] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 3];
4327                                 }
4328                                 for (j = 0;j < out->num_triangles*3;j++)
4329                                         (out->groupmesh->data_element3i + 3 * out->num_firsttriangle)[j] = loadmodel->brushq3.data_element3i[firstelement + j] + out->num_firstvertex;
4330                                 break;
4331                         case Q3FACETYPE_PATCH:
4332                                 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
4333                                 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
4334                                 originalvertex3f = loadmodel->brushq3.data_vertex3f + firstvertex * 3;
4335                                 originaltexcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + firstvertex * 2;
4336                                 originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + firstvertex * 2;
4337                                 originalcolor4f = loadmodel->brushq3.data_color4f + firstvertex * 4;
4338                                 // convert patch to Q3FACETYPE_MESH
4339                                 xtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
4340                                 ytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
4341                                 // bound to user settings
4342                                 xtess = bound(r_subdivisions_mintess.integer, xtess, r_subdivisions_maxtess.integer);
4343                                 ytess = bound(r_subdivisions_mintess.integer, ytess, r_subdivisions_maxtess.integer);
4344                                 // bound to sanity settings
4345                                 xtess = bound(1, xtess, 1024);
4346                                 ytess = bound(1, ytess, 1024);
4347                                 // bound to user limit on vertices
4348                                 while ((xtess > 1 || ytess > 1) && (((patchsize[0] - 1) * xtess) + 1) * (((patchsize[1] - 1) * ytess) + 1) > min(r_subdivisions_maxvertices.integer, 262144))
4349                                 {
4350                                         if (xtess > ytess)
4351                                                 xtess--;
4352                                         else
4353                                                 ytess--;
4354                                 }
4355                                 finalwidth = ((patchsize[0] - 1) * xtess) + 1;
4356                                 finalheight = ((patchsize[1] - 1) * ytess) + 1;
4357                                 finalvertices = finalwidth * finalheight;
4358                                 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
4359                                 type = Q3FACETYPE_MESH;
4360                                 // generate geometry
4361                                 // (note: normals are skipped because they get recalculated)
4362                                 Q3PatchTesselateFloat(3, sizeof(float[3]), (out->groupmesh->data_vertex3f + 3 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[3]), originalvertex3f, xtess, ytess);
4363                                 Q3PatchTesselateFloat(2, sizeof(float[2]), (out->groupmesh->data_texcoordtexture2f + 2 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[2]), originaltexcoordtexture2f, xtess, ytess);
4364                                 Q3PatchTesselateFloat(2, sizeof(float[2]), (out->groupmesh->data_texcoordlightmap2f + 2 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[2]), originaltexcoordlightmap2f, xtess, ytess);
4365                                 Q3PatchTesselateFloat(4, sizeof(float[4]), (out->groupmesh->data_lightmapcolor4f + 4 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[4]), originalcolor4f, xtess, ytess);
4366                                 Q3PatchTriangleElements((out->groupmesh->data_element3i + 3 * out->num_firsttriangle), finalwidth, finalheight, out->num_firstvertex);
4367                                 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);
4368                                 if (developer.integer >= 2)
4369                                 {
4370                                         if (out->num_triangles < finaltriangles)
4371                                                 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);
4372                                         else
4373                                                 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);
4374                                 }
4375                                 // q3map does not put in collision brushes for curves... ugh
4376                                 // build the lower quality collision geometry
4377                                 xtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value);
4378                                 ytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value);
4379                                 // bound to user settings
4380                                 xtess = bound(r_subdivisions_collision_mintess.integer, xtess, r_subdivisions_collision_maxtess.integer);
4381                                 ytess = bound(r_subdivisions_collision_mintess.integer, ytess, r_subdivisions_collision_maxtess.integer);
4382                                 // bound to sanity settings
4383                                 xtess = bound(1, xtess, 1024);
4384                                 ytess = bound(1, ytess, 1024);
4385                                 // bound to user limit on vertices
4386                                 while ((xtess > 1 || ytess > 1) && (((patchsize[0] - 1) * xtess) + 1) * (((patchsize[1] - 1) * ytess) + 1) > min(r_subdivisions_collision_maxvertices.integer, 262144))
4387                                 {
4388                                         if (xtess > ytess)
4389                                                 xtess--;
4390                                         else
4391                                                 ytess--;
4392                                 }
4393                                 finalwidth = ((patchsize[0] - 1) * xtess) + 1;
4394                                 finalheight = ((patchsize[1] - 1) * ytess) + 1;
4395                                 finalvertices = finalwidth * finalheight;
4396                                 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
4397
4398                                 out->data_collisionvertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * finalvertices);
4399                                 out->data_collisionelement3i = Mem_Alloc(loadmodel->mempool, sizeof(int[3]) * finaltriangles);
4400                                 out->num_collisionvertices = finalvertices;
4401                                 out->num_collisiontriangles = finaltriangles;
4402                                 Q3PatchTesselateFloat(3, sizeof(float[3]), out->data_collisionvertex3f, patchsize[0], patchsize[1], sizeof(float[3]), originalvertex3f, xtess, ytess);
4403                                 Q3PatchTriangleElements(out->data_collisionelement3i, finalwidth, finalheight, 0);
4404
4405                                 //Mod_SnapVertices(3, out->num_vertices, (out->groupmesh->data_vertex3f + 3 * out->num_firstvertex), 0.25);
4406                                 Mod_SnapVertices(3, out->num_collisionvertices, out->data_collisionvertex3f, 1);
4407
4408                                 oldnumtriangles = out->num_triangles;
4409                                 oldnumtriangles2 = out->num_collisiontriangles;
4410                                 out->num_collisiontriangles = Mod_RemoveDegenerateTriangles(out->num_collisiontriangles, out->data_collisionelement3i, out->data_collisionelement3i, out->data_collisionvertex3f);
4411                                 if (developer.integer)
4412                                         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);
4413                                 break;
4414                         default:
4415                                 break;
4416                         }
4417                         meshvertices += out->num_vertices;
4418                         meshtriangles += out->num_triangles;
4419                         for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++)
4420                                 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)
4421                                         invalidelements++;
4422                         if (invalidelements)
4423                         {
4424                                 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);
4425                                 for (j = 0;j < out->num_triangles * 3;j++)
4426                                 {
4427                                         Con_Printf(" %i", (out->groupmesh->data_element3i + 3 * out->num_firsttriangle)[j] - out->num_firstvertex);
4428                                         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)
4429                                                 (out->groupmesh->data_element3i + 3 * out->num_firsttriangle)[j] = out->num_firstvertex;
4430                                 }
4431                                 Con_Print("\n");
4432                         }
4433                         // for per pixel lighting
4434                         Mod_BuildTextureVectorsAndNormals(out->num_firstvertex, out->num_vertices, out->num_triangles, out->groupmesh->data_vertex3f, out->groupmesh->data_texcoordtexture2f, (out->groupmesh->data_element3i + 3 * out->num_firsttriangle), out->groupmesh->data_svector3f, out->groupmesh->data_tvector3f, out->groupmesh->data_normal3f, true);
4435                         // calculate a bounding box
4436                         VectorClear(out->mins);
4437                         VectorClear(out->maxs);
4438                         if (out->num_vertices)
4439                         {
4440                                 VectorCopy((out->groupmesh->data_vertex3f + 3 * out->num_firstvertex), out->mins);
4441                                 VectorCopy((out->groupmesh->data_vertex3f + 3 * out->num_firstvertex), out->maxs);
4442                                 for (j = 1, v = (out->groupmesh->data_vertex3f + 3 * out->num_firstvertex) + 3;j < out->num_vertices;j++, v += 3)
4443                                 {
4444                                         out->mins[0] = min(out->mins[0], v[0]);
4445                                         out->maxs[0] = max(out->maxs[0], v[0]);
4446                                         out->mins[1] = min(out->mins[1], v[1]);
4447                                         out->maxs[1] = max(out->maxs[1], v[1]);
4448                                         out->mins[2] = min(out->mins[2], v[2]);
4449                                         out->maxs[2] = max(out->maxs[2], v[2]);
4450                                 }
4451                                 out->mins[0] -= 1.0f;
4452                                 out->mins[1] -= 1.0f;
4453                                 out->mins[2] -= 1.0f;
4454                                 out->maxs[0] += 1.0f;
4455                                 out->maxs[1] += 1.0f;
4456                                 out->maxs[2] += 1.0f;
4457                         }
4458                         // set lightmap styles for consistency with q1bsp
4459                         //out->lightmapinfo->styles[0] = 0;
4460                         //out->lightmapinfo->styles[1] = 255;
4461                         //out->lightmapinfo->styles[2] = 255;
4462                         //out->lightmapinfo->styles[3] = 255;
4463                 }
4464         }
4465
4466         // now store the completed list of meshes
4467         loadmodel->nummeshes = meshnum;
4468         if (loadmodel->nummeshes)
4469         {
4470                 loadmodel->meshlist = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t *) * loadmodel->nummeshes);
4471                 memcpy(loadmodel->meshlist, tempmeshlist, sizeof(surfmesh_t *) * loadmodel->nummeshes);
4472         }
4473
4474         // free the no longer needed vertex data
4475         loadmodel->brushq3.num_vertices = 0;
4476         Mem_Free(loadmodel->brushq3.data_vertex3f);
4477         loadmodel->brushq3.data_vertex3f = NULL;
4478         loadmodel->brushq3.data_texcoordtexture2f = NULL;
4479         loadmodel->brushq3.data_texcoordlightmap2f = NULL;
4480         loadmodel->brushq3.data_color4f = NULL;
4481         // free the no longer needed triangle data
4482         loadmodel->brushq3.num_triangles = 0;
4483         Mem_Free(loadmodel->brushq3.data_element3i);
4484         loadmodel->brushq3.data_element3i = NULL;
4485 }
4486
4487 static void Mod_Q3BSP_LoadModels(lump_t *l)
4488 {
4489         q3dmodel_t *in;
4490         q3dmodel_t *out;
4491         int i, j, n, c, count;
4492
4493         in = (void *)(mod_base + l->fileofs);
4494         if (l->filelen % sizeof(*in))
4495                 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
4496         count = l->filelen / sizeof(*in);
4497         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4498
4499         loadmodel->brushq3.data_models = out;
4500         loadmodel->brushq3.num_models = count;
4501
4502         for (i = 0;i < count;i++, in++, out++)
4503         {
4504                 for (j = 0;j < 3;j++)
4505                 {
4506                         out->mins[j] = LittleFloat(in->mins[j]);
4507                         out->maxs[j] = LittleFloat(in->maxs[j]);
4508                 }
4509                 n = LittleLong(in->firstface);
4510                 c = LittleLong(in->numfaces);
4511                 if (n < 0 || n + c > loadmodel->num_surfaces)
4512                         Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->num_surfaces);
4513                 out->firstface = n;
4514                 out->numfaces = c;
4515                 n = LittleLong(in->firstbrush);
4516                 c = LittleLong(in->numbrushes);
4517                 if (n < 0 || n + c > loadmodel->brush.num_brushes)
4518                         Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brush.num_brushes);
4519                 out->firstbrush = n;
4520                 out->numbrushes = c;
4521         }
4522 }
4523
4524 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
4525 {
4526         int *in;
4527         int *out;
4528         int i, n, count;
4529
4530         in = (void *)(mod_base + l->fileofs);
4531         if (l->filelen % sizeof(*in))
4532                 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
4533         count = l->filelen / sizeof(*in);
4534         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4535
4536         loadmodel->brush.data_leafbrushes = out;
4537         loadmodel->brush.num_leafbrushes = count;
4538
4539         for (i = 0;i < count;i++, in++, out++)
4540         {
4541                 n = LittleLong(*in);
4542                 if (n < 0 || n >= loadmodel->brush.num_brushes)
4543                         Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brush.num_brushes);
4544                 *out = n;
4545         }
4546 }
4547
4548 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
4549 {
4550         int *in;
4551         int *out;
4552         int i, n, count;
4553
4554         in = (void *)(mod_base + l->fileofs);
4555         if (l->filelen % sizeof(*in))
4556                 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
4557         count = l->filelen / sizeof(*in);
4558         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4559
4560         loadmodel->brush.data_leafsurfaces = out;
4561         loadmodel->brush.num_leafsurfaces = count;
4562
4563         for (i = 0;i < count;i++, in++, out++)
4564         {
4565                 n = LittleLong(*in);
4566                 if (n < 0 || n >= loadmodel->num_surfaces)
4567                         Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->num_surfaces);
4568                 *out = n;
4569         }
4570 }
4571
4572 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
4573 {
4574         q3dleaf_t *in;
4575         mleaf_t *out;
4576         int i, j, n, c, count;
4577
4578         in = (void *)(mod_base + l->fileofs);
4579         if (l->filelen % sizeof(*in))
4580                 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
4581         count = l->filelen / sizeof(*in);
4582         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4583
4584         loadmodel->brush.data_leafs = out;
4585         loadmodel->brush.num_leafs = count;
4586
4587         for (i = 0;i < count;i++, in++, out++)
4588         {
4589                 out->parent = NULL;
4590                 out->plane = NULL;
4591                 out->clusterindex = LittleLong(in->clusterindex);
4592                 out->areaindex = LittleLong(in->areaindex);
4593                 for (j = 0;j < 3;j++)
4594                 {
4595                         // yes the mins/maxs are ints
4596                         out->mins[j] = LittleLong(in->mins[j]) - 1;
4597                         out->maxs[j] = LittleLong(in->maxs[j]) + 1;
4598                 }
4599                 n = LittleLong(in->firstleafface);
4600                 c = LittleLong(in->numleaffaces);
4601                 if (n < 0 || n + c > loadmodel->brush.num_leafsurfaces)
4602                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafsurface range %i : %i (%i leafsurfaces)\n", n, n + c, loadmodel->brush.num_leafsurfaces);
4603                 out->firstleafsurface = loadmodel->brush.data_leafsurfaces + n;
4604                 out->numleafsurfaces = c;
4605                 n = LittleLong(in->firstleafbrush);
4606                 c = LittleLong(in->numleafbrushes);
4607                 if (n < 0 || n + c > loadmodel->brush.num_leafbrushes)
4608                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brush.num_leafbrushes);
4609                 out->firstleafbrush = loadmodel->brush.data_leafbrushes + n;
4610                 out->numleafbrushes = c;
4611         }
4612 }
4613
4614 static void Mod_Q3BSP_LoadNodes(lump_t *l)
4615 {
4616         q3dnode_t *in;
4617         mnode_t *out;
4618         int i, j, n, count;
4619
4620         in = (void *)(mod_base + l->fileofs);
4621         if (l->filelen % sizeof(*in))
4622                 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
4623         count = l->filelen / sizeof(*in);
4624         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4625
4626         loadmodel->brush.data_nodes = out;
4627         loadmodel->brush.num_nodes = count;
4628
4629         for (i = 0;i < count;i++, in++, out++)
4630         {
4631                 out->parent = NULL;
4632                 n = LittleLong(in->planeindex);
4633                 if (n < 0 || n >= loadmodel->brush.num_planes)
4634                         Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brush.num_planes);
4635                 out->plane = loadmodel->brush.data_planes + n;
4636                 for (j = 0;j < 2;j++)
4637                 {
4638                         n = LittleLong(in->childrenindex[j]);
4639                         if (n >= 0)
4640                         {
4641                                 if (n >= loadmodel->brush.num_nodes)
4642                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brush.num_nodes);
4643                                 out->children[j] = loadmodel->brush.data_nodes + n;
4644                         }
4645                         else
4646                         {
4647                                 n = -1 - n;
4648                                 if (n >= loadmodel->brush.num_leafs)
4649                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brush.num_leafs);
4650                                 out->children[j] = (mnode_t *)(loadmodel->brush.data_leafs + n);
4651                         }
4652                 }
4653                 for (j = 0;j < 3;j++)
4654                 {
4655                         // yes the mins/maxs are ints
4656                         out->mins[j] = LittleLong(in->mins[j]) - 1;
4657                         out->maxs[j] = LittleLong(in->maxs[j]) + 1;
4658                 }
4659         }
4660
4661         // set the parent pointers
4662         Mod_Q1BSP_LoadNodes_RecursiveSetParent(loadmodel->brush.data_nodes, NULL);
4663 }
4664
4665 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
4666 {
4667         q3dlightgrid_t *in;
4668         q3dlightgrid_t *out;
4669         int count;
4670
4671         in = (void *)(mod_base + l->fileofs);
4672         if (l->filelen % sizeof(*in))
4673                 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
4674         loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
4675         loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
4676         loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
4677         loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4678         loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4679         loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4680         loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4681         loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4682         loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4683         loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4684         loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4685         loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4686         count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4687         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]);
4688         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]);
4689
4690         // if lump is empty there is nothing to load, we can deal with that in the LightPoint code
4691         if (l->filelen)
4692         {
4693                 if (l->filelen < count * (int)sizeof(*in))
4694                         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]);
4695                 if (l->filelen != count * (int)sizeof(*in))
4696                         Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4697                 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4698                 loadmodel->brushq3.data_lightgrid = out;
4699                 loadmodel->brushq3.num_lightgrid = count;
4700                 // no swapping or validation necessary
4701                 memcpy(out, in, count * (int)sizeof(*out));
4702         }
4703 }
4704
4705 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4706 {
4707         q3dpvs_t *in;
4708         int totalchains;
4709
4710         if (l->filelen == 0)
4711         {
4712                 int i;
4713                 // unvised maps often have cluster indices even without pvs, so check
4714                 // leafs to find real number of clusters
4715                 loadmodel->brush.num_pvsclusters = 1;
4716                 for (i = 0;i < loadmodel->brush.num_leafs;i++)
4717                         loadmodel->brush.num_pvsclusters = max(loadmodel->brush.num_pvsclusters, loadmodel->brush.data_leafs[i].clusterindex + 1);
4718
4719                 // create clusters
4720                 loadmodel->brush.num_pvsclusterbytes = (loadmodel->brush.num_pvsclusters + 7) / 8;
4721                 totalchains = loadmodel->brush.num_pvsclusterbytes * loadmodel->brush.num_pvsclusters;
4722                 loadmodel->brush.data_pvsclusters = Mem_Alloc(loadmodel->mempool, totalchains);
4723                 memset(loadmodel->brush.data_pvsclusters, 0xFF, totalchains);
4724                 return;
4725         }
4726
4727         in = (void *)(mod_base + l->fileofs);
4728         if (l->filelen < 9)
4729                 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4730
4731         loadmodel->brush.num_pvsclusters = LittleLong(in->numclusters);
4732         loadmodel->brush.num_pvsclusterbytes = LittleLong(in->chainlength);
4733         if (loadmodel->brush.num_pvsclusterbytes < ((loadmodel->brush.num_pvsclusters + 7) / 8))
4734                 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brush.num_pvsclusterbytes, loadmodel->brush.num_pvsclusters);
4735         totalchains = loadmodel->brush.num_pvsclusterbytes * loadmodel->brush.num_pvsclusters;
4736         if (l->filelen < totalchains + (int)sizeof(*in))
4737                 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);
4738
4739         loadmodel->brush.data_pvsclusters = Mem_Alloc(loadmodel->mempool, totalchains);
4740         memcpy(loadmodel->brush.data_pvsclusters, (qbyte *)(in + 1), totalchains);
4741 }
4742
4743 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4744 {
4745         int i, j, k, index[3];
4746         float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4747         q3dlightgrid_t *a, *s;
4748         if (!model->brushq3.num_lightgrid)
4749         {
4750                 ambientcolor[0] = 1;
4751                 ambientcolor[1] = 1;
4752                 ambientcolor[2] = 1;
4753                 return;
4754         }
4755         Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4756         //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4757         //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4758         transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0] - 1);
4759         transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1] - 1);
4760         transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2] - 1);
4761         index[0] = (int)floor(transformed[0]);
4762         index[1] = (int)floor(transformed[1]);
4763         index[2] = (int)floor(transformed[2]);
4764         //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4765         // now lerp the values
4766         VectorClear(diffusenormal);
4767         a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4768         for (k = 0;k < 2;k++)
4769         {
4770                 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4771                 if (blend1 < 0.001f || index[2] + k >= model->brushq3.num_lightgrid_isize[2])
4772                         continue;
4773                 for (j = 0;j < 2;j++)
4774                 {
4775                         blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4776                         if (blend2 < 0.001f || index[1] + j >= model->brushq3.num_lightgrid_isize[1])
4777                                 continue;
4778                         for (i = 0;i < 2;i++)
4779                         {
4780                                 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4781                                 if (blend < 0.001f || index[0] + i >= model->brushq3.num_lightgrid_isize[0])
4782                                         continue;
4783                                 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4784                                 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4785                                 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4786                                 pitch = s->diffusepitch * M_PI / 128;
4787                                 yaw = s->diffuseyaw * M_PI / 128;
4788                                 sinpitch = sin(pitch);
4789                                 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4790                                 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4791                                 diffusenormal[2] += blend * (cos(pitch));
4792                                 //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)));
4793                         }
4794                 }
4795         }
4796         VectorNormalize(diffusenormal);
4797         //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]);
4798 }
4799
4800 static void Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace_t *trace, model_t *model, mnode_t *node, const vec3_t point, int markframe)
4801 {
4802         int i;
4803         mleaf_t *leaf;
4804         colbrushf_t *brush;
4805         // find which leaf the point is in
4806         while (node->plane)
4807                 node = node->children[DotProduct(point, node->plane->normal) < node->plane->dist];
4808         // point trace the brushes
4809         leaf = (mleaf_t *)node;
4810         for (i = 0;i < leaf->numleafbrushes;i++)
4811         {
4812                 brush = model->brush.data_brushes[leaf->firstleafbrush[i]].colbrushf;
4813                 if (brush && brush->markframe != markframe && BoxesOverlap(point, point, brush->mins, brush->maxs))
4814                 {
4815                         brush->markframe = markframe;
4816                         Collision_TracePointBrushFloat(trace, point, brush);
4817                 }
4818         }
4819         // can't do point traces on curves (they have no thickness)
4820 }
4821
4822 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)
4823 {
4824         int i, startside, endside;
4825         float dist1, dist2, midfrac, mid[3], nodesegmentmins[3], nodesegmentmaxs[3];
4826         mleaf_t *leaf;
4827         msurface_t *surface;
4828         colbrushf_t *brush;
4829         if (startfrac > trace->realfraction)
4830                 return;
4831         // note: all line fragments past first impact fraction are ignored
4832         if (VectorCompare(start, end))
4833         {
4834                 // find which leaf the point is in
4835                 while (node->plane)
4836                         node = node->children[DotProduct(start, node->plane->normal) < node->plane->dist];
4837         }
4838         else
4839         {
4840                 // find which nodes the line is in and recurse for them
4841                 while (node->plane)
4842                 {
4843                         // recurse down node sides
4844                         dist1 = PlaneDiff(start, node->plane);
4845                         dist2 = PlaneDiff(end, node->plane);
4846                         startside = dist1 < 0;
4847                         endside = dist2 < 0;
4848                         if (startside == endside)
4849                         {
4850                                 // most of the time the line fragment is on one side of the plane
4851                                 node = node->children[startside];
4852                         }
4853                         else
4854                         {
4855                                 // line crosses node plane, split the line
4856                                 midfrac = dist1 / (dist1 - dist2);
4857                                 VectorLerp(start, midfrac, end, mid);
4858                                 // take the near side first
4859                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
4860                                 if (midfrac <= trace->realfraction)
4861                                         Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
4862                                 return;
4863                         }
4864                 }
4865         }
4866         // hit a leaf
4867         nodesegmentmins[0] = min(start[0], end[0]);
4868         nodesegmentmins[1] = min(start[1], end[1]);
4869         nodesegmentmins[2] = min(start[2], end[2]);
4870         nodesegmentmaxs[0] = max(start[0], end[0]);
4871         nodesegmentmaxs[1] = max(start[1], end[1]);
4872         nodesegmentmaxs[2] = max(start[2], end[2]);
4873         // line trace the brushes
4874         leaf = (mleaf_t *)node;
4875         for (i = 0;i < leaf->numleafbrushes;i++)
4876         {
4877                 brush = model->brush.data_brushes[leaf->firstleafbrush[i]].colbrushf;
4878                 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
4879                 {
4880                         brush->markframe = markframe;
4881                         Collision_TraceLineBrushFloat(trace, linestart, lineend, brush, brush);
4882                         if (startfrac > trace->realfraction)
4883                                 return;
4884                 }
4885         }
4886         // can't do point traces on curves (they have no thickness)
4887         if (mod_q3bsp_curves_collisions.integer && !VectorCompare(start, end))
4888         {
4889                 // line trace the curves
4890                 for (i = 0;i < leaf->numleafsurfaces;i++)
4891                 {
4892                         surface = model->data_surfaces + leaf->firstleafsurface[i];
4893                         if (surface->num_collisiontriangles && surface->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, surface->mins, surface->maxs))
4894                         {
4895                                 surface->collisionmarkframe = markframe;
4896                                 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, segmentmins, segmentmaxs);
4897                                 if (startfrac > trace->realfraction)
4898                                         return;
4899                         }
4900                 }
4901         }
4902 }
4903
4904 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)
4905 {
4906         int i;
4907         //int sides;
4908         float nodesegmentmins[3], nodesegmentmaxs[3];
4909         mleaf_t *leaf;
4910         colbrushf_t *brush;
4911         msurface_t *surface;
4912         /*
4913                 // find which nodes the line is in and recurse for them
4914                 while (node->plane)
4915                 {
4916                         // recurse down node sides
4917                         int startside, endside;
4918                         float dist1near, dist1far, dist2near, dist2far;
4919                         BoxPlaneCornerDistances(thisbrush_start->mins, thisbrush_start->maxs, node->plane, &dist1near, &dist1far);
4920                         BoxPlaneCornerDistances(thisbrush_end->mins, thisbrush_end->maxs, node->plane, &dist2near, &dist2far);
4921                         startside = dist1near < 0;
4922                         startside = dist1near < 0 ? (dist1far < 0 ? 1 : 2) : (dist1far < 0 ? 2 : 0);
4923                         endside = dist2near < 0 ? (dist2far < 0 ? 1 : 2) : (dist2far < 0 ? 2 : 0);
4924                         if (startside == 2 || endside == 2)
4925                         {
4926                                 // brushes cross plane
4927                                 // do not clip anything, just take both sides
4928                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4929                                 node = node->children[1];
4930                                 continue;
4931                         }
4932                         if (startside == 0)
4933                         {
4934                                 if (endside == 0)
4935                                 {
4936                                         node = node->children[0];
4937                                         continue;
4938                                 }
4939                                 else
4940                                 {
4941                                         //midf0 = dist1near / (dist1near - dist2near);
4942                                         //midf1 = dist1far / (dist1far - dist2far);
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                         }
4948                         else
4949                         {
4950                                 if (endside == 0)
4951                                 {
4952                                         //midf0 = dist1near / (dist1near - dist2near);
4953                                         //midf1 = dist1far / (dist1far - dist2far);
4954                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4955                                         node = node->children[1];
4956                                         continue;
4957                                 }
4958                                 else
4959                                 {
4960                                         node = node->children[1];
4961                                         continue;
4962                                 }
4963                         }
4964
4965                         if (dist1near <  0 && dist2near <  0 && dist1far <  0 && dist2far <  0){node = node->children[1];continue;}
4966                         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;}
4967                         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;}
4968                         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;}
4969                         if (dist1near <  0 && dist2near >= 0 && dist1far <  0 && dist2far <  0){node = node->children[1];continue;}
4970                         if (dist1near <  0 && dist2near >= 0 && dist1far <  0 && dist2far >= 0){}
4971                         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;}
4972                         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;}
4973                         if (dist1near >= 0 && dist2near <  0 && dist1far <  0 && dist2far <  0){node = node->children[1];continue;}
4974                         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;}
4975                         if (dist1near >= 0 && dist2near <  0 && dist1far >= 0 && dist2far <  0){}
4976                         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;}
4977                         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;}
4978                         if (dist1near >= 0 && dist2near >= 0 && dist1far <  0 && dist2far >= 0){node = node->children[0];continue;}
4979                         if (dist1near >= 0 && dist2near >= 0 && dist1far >= 0 && dist2far <  0){node = node->children[0];continue;}
4980                         if (dist1near >= 0 && dist2near >= 0 && dist1far >= 0 && dist2far >= 0){node = node->children[0];continue;}
4981                         {
4982                                 if (dist2near < 0) // d1n<0 && d2n<0
4983                                 {
4984                                         if (dist2near < 0) // d1n<0 && d2n<0
4985                                         {
4986                                                 if (dist2near < 0) // d1n<0 && d2n<0
4987                                                 {
4988                                                 }
4989                                                 else // d1n<0 && d2n>0
4990                                                 {
4991                                                 }
4992                                         }
4993                                         else // d1n<0 && d2n>0
4994                                         {
4995                                                 if (dist2near < 0) // d1n<0 && d2n<0
4996                                                 {
4997                                                 }
4998                                                 else // d1n<0 && d2n>0
4999                                                 {
5000                                                 }
5001                                         }
5002                                 }
5003                                 else // d1n<0 && d2n>0
5004                                 {
5005                                 }
5006                         }
5007                         else // d1n>0
5008                         {
5009                                 if (dist2near < 0) // d1n>0 && d2n<0
5010                                 {
5011                                 }
5012                                 else // d1n>0 && d2n>0
5013                                 {
5014                                 }
5015                         }
5016                         if (dist1near < 0 == dist1far < 0 == dist2near < 0 == dist2far < 0)
5017                         {
5018                                 node = node->children[startside];
5019                                 continue;
5020                         }
5021                         if (dist1near < dist2near)
5022                         {
5023                                 // out
5024                                 if (dist1near >= 0)
5025                                 {
5026                                         node = node->children[0];
5027                                         continue;
5028                                 }
5029                                 if (dist2far < 0)
5030                                 {
5031                                         node = node->children[1];
5032                                         continue;
5033                                 }
5034                                 // dist1near < 0 && dist2far >= 0
5035                         }
5036                         else
5037                         {
5038                                 // in
5039                         }
5040                         startside = dist1near < 0 ? (dist1far < 0 ? 1 : 2) : (dist1far < 0 ? 2 : 0);
5041                         endside = dist2near < 0 ? (dist2far < 0 ? 1 : 2) : (dist2far < 0 ? 2 : 0);
5042                         if (startside == 2 || endside == 2)
5043                         {
5044                                 // brushes cross plane
5045                                 // do not clip anything, just take both sides
5046                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5047                                 node = node->children[1];
5048                         }
5049                         else if (startside == endside)
5050                                 node = node->children[startside];
5051                         else if (startside == 0) // endside = 1 (start infront, end behind)
5052                         {
5053                         }
5054                         else // startside == 1 endside = 0 (start behind, end infront)
5055                         {
5056                         }
5057                         == endside)
5058                         {
5059                                 if (startside < 2)
5060                                         node = node->children[startside];
5061                                 else
5062                                 {
5063                                         // start and end brush cross plane
5064                                 }
5065                         }
5066                         else
5067                         {
5068                         }
5069                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5070                                 node = node->children[1];
5071                         else if (dist1near < 0 && dist1far < 0 && dist2near >= 0 && dist2far >= 0)
5072                         else if (dist1near >= 0 && dist1far >= 0 && dist2near < 0 && dist2far < 0)
5073                         else if (dist1near >= 0 && dist1far >= 0 && dist2near >= 0 && dist2far >= 0)
5074                                 node = node->children[0];
5075                         else
5076                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5077                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5078                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5079                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5080                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5081                         {
5082                         }
5083                         else if (dist1near >= 0 && dist1far >= 0)
5084                         {
5085                         }
5086                         else // mixed (lying on plane)
5087                         {
5088                         }
5089                         {
5090                                 if (dist2near < 0 && dist2far < 0)
5091                                 {
5092                                 }
5093                                 else
5094                                         node = node->children[1];
5095                         }
5096                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5097                                 node = node->children[0];
5098                         else if (dist1near >= 0 && dist1far >= 0 && dist2near >= 0 && dist2far >= 0)
5099                                 node = node->children[1];
5100                         else
5101                         {
5102                                 // both sides
5103                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
5104                                 node = node->children[1];
5105                         }
5106                         sides = dist1near || dist1near < 0 | dist1far < 0 | dist2near < 0 | dist
5107                         startside = dist1 < 0;
5108                         endside = dist2 < 0;
5109                         if (startside == endside)
5110                         {
5111                                 // most of the time the line fragment is on one side of the plane
5112                                 node = node->children[startside];
5113                         }
5114                         else
5115                         {
5116                                 // line crosses node plane, split the line
5117                                 midfrac = dist1 / (dist1 - dist2);
5118                                 VectorLerp(start, midfrac, end, mid);
5119                                 // take the near side first
5120                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
5121                                 if (midfrac <= trace->fraction)
5122                                         Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
5123                                 return;
5124                         }
5125                 }
5126         */
5127 #if 1
5128         for (;;)
5129         {
5130                 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5131                 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5132                 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5133                 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5134                 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5135                 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5136                 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
5137                         return;
5138                 if (!node->plane)
5139                         break;
5140                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5141                 node = node->children[1];
5142         }
5143 #elif 0
5144         // FIXME: could be made faster by copying TraceLine code and making it use
5145         // box plane distances...  (variant on the BoxOnPlaneSide code)
5146         for (;;)
5147         {
5148                 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5149                 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5150                 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5151                 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5152                 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5153                 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5154                 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
5155                         return;
5156                 if (!node->plane)
5157                         break;
5158                 if (mod_q3bsp_debugtracebrush.integer == 2)
5159                 {
5160                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5161                         node = node->children[1];
5162                         continue;
5163                 }
5164                 else if (mod_q3bsp_debugtracebrush.integer == 1)
5165                 {
5166                         // recurse down node sides
5167                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5168                         if (sides == 3)
5169                         {
5170                                 // segment box crosses plane
5171                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5172                                 node = node->children[1];
5173                                 continue;
5174                         }
5175                         // take whichever side the segment box is on
5176                         node = node->children[sides - 1];
5177                         continue;
5178                 }
5179                 else
5180                 {
5181                         // recurse down node sides
5182                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5183                         if (sides == 3)
5184                         {
5185                                 // segment box crosses plane
5186                                 // now check start and end brush boxes to handle a lot of 'diagonal' cases more efficiently...
5187                                 sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, node->plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, node->plane);
5188                                 if (sides == 3)
5189                                 {
5190                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5191                                         node = node->children[1];
5192                                         continue;
5193                                 }
5194                         }
5195                         // take whichever side the segment box is on
5196                         node = node->children[sides - 1];
5197                         continue;
5198                 }
5199                 return;
5200         }
5201 #else
5202         // FIXME: could be made faster by copying TraceLine code and making it use
5203         // box plane distances...  (variant on the BoxOnPlaneSide code)
5204         for (;;)
5205         {
5206                 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5207                 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5208                 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5209                 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5210                 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5211                 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5212                 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
5213                         return;
5214                 if (!node->plane)
5215                         break;
5216                 if (mod_q3bsp_debugtracebrush.integer == 2)
5217                 {
5218                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5219                         node = node->children[1];
5220                 }
5221                 else if (mod_q3bsp_debugtracebrush.integer == 1)
5222                 {
5223                         // recurse down node sides
5224                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5225                         if (sides == 3)
5226                         {
5227                                 // segment box crosses plane
5228                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5229                                 node = node->children[1];
5230                         }
5231                         else
5232                         {
5233                                 // take whichever side the segment box is on
5234                                 node = node->children[sides - 1];
5235                         }
5236                 }
5237                 else
5238                 {
5239                         // recurse down node sides
5240                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5241                         if (sides == 3)
5242                         {
5243                                 // segment box crosses plane
5244                                 // now check start and end brush boxes to handle a lot of 'diagonal' cases more efficiently...
5245                                 sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, node->plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, node->plane);
5246                                 if (sides == 3)
5247                                 {
5248                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5249                                         sides = 2;
5250                                 }
5251                         }
5252                         // take whichever side the segment box is on
5253                         node = node->children[sides - 1];
5254                 }
5255         }
5256 #endif
5257         // hit a leaf
5258         leaf = (mleaf_t *)node;
5259         for (i = 0;i < leaf->numleafbrushes;i++)
5260         {
5261                 brush = model->brush.data_brushes[leaf->firstleafbrush[i]].colbrushf;
5262                 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
5263                 {
5264                         brush->markframe = markframe;
5265                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, brush, brush);
5266                 }
5267         }
5268         if (mod_q3bsp_curves_collisions.integer)
5269         {
5270                 for (i = 0;i < leaf->numleafsurfaces;i++)
5271                 {
5272                         surface = model->data_surfaces + leaf->firstleafsurface[i];
5273                         if (surface->num_collisiontriangles && surface->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, surface->mins, surface->maxs))
5274                         {
5275                                 surface->collisionmarkframe = markframe;
5276                                 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, segmentmins, segmentmaxs);
5277                         }
5278                 }
5279         }
5280 }
5281
5282 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)
5283 {
5284         int i;
5285         float segmentmins[3], segmentmaxs[3];
5286         colbrushf_t *thisbrush_start, *thisbrush_end;
5287         matrix4x4_t startmatrix, endmatrix;
5288         static int markframe = 0;
5289         msurface_t *surface;
5290         q3mbrush_t *brush;
5291         memset(trace, 0, sizeof(*trace));
5292         trace->fraction = 1;
5293         trace->realfraction = 1;
5294         trace->hitsupercontentsmask = hitsupercontentsmask;
5295         Matrix4x4_CreateIdentity(&startmatrix);
5296         Matrix4x4_CreateIdentity(&endmatrix);
5297         segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
5298         segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
5299         segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
5300         segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
5301         segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
5302         segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
5303         if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
5304         {
5305                 if (VectorCompare(boxstartmins, boxendmins))
5306                 {
5307                         // point trace
5308                         if (model->brush.submodel)
5309                         {
5310                                 for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
5311                                         if (brush->colbrushf)
5312                                                 Collision_TracePointBrushFloat(trace, boxstartmins, brush->colbrushf);
5313                         }
5314                         else
5315                                 Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace, model, model->brush.data_nodes, boxstartmins, ++markframe);
5316                 }
5317                 else
5318                 {
5319                         // line trace
5320                         if (model->brush.submodel)
5321                         {
5322                                 for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
5323                                         if (brush->colbrushf)
5324                                                 Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, brush->colbrushf, brush->colbrushf);
5325                                 if (mod_q3bsp_curves_collisions.integer)
5326                                         for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
5327                                                 if (surface->num_collisiontriangles)
5328                                                         Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, segmentmins, segmentmaxs);
5329                         }
5330                         else
5331                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, model->brush.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe, segmentmins, segmentmaxs);
5332                 }
5333         }
5334         else
5335         {
5336                 // box trace, performed as brush trace
5337                 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
5338                 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
5339                 if (model->brush.submodel)
5340                 {
5341                         for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
5342                                 if (brush->colbrushf)
5343                                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, brush->colbrushf, brush->colbrushf);
5344                         if (mod_q3bsp_curves_collisions.integer)
5345                                 for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
5346                                         if (surface->num_collisiontriangles)
5347                                                 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, segmentmins, segmentmaxs);
5348                 }
5349                 else
5350                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, model->brush.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
5351         }
5352 }
5353
5354 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
5355 {
5356         int supercontents = 0;
5357         if (nativecontents & CONTENTSQ3_SOLID)
5358                 supercontents |= SUPERCONTENTS_SOLID;
5359         if (nativecontents & CONTENTSQ3_WATER)
5360                 supercontents |= SUPERCONTENTS_WATER;
5361         if (nativecontents & CONTENTSQ3_SLIME)
5362                 supercontents |= SUPERCONTENTS_SLIME;
5363         if (nativecontents & CONTENTSQ3_LAVA)
5364                 supercontents |= SUPERCONTENTS_LAVA;
5365         if (nativecontents & CONTENTSQ3_BODY)
5366                 supercontents |= SUPERCONTENTS_BODY;
5367         if (nativecontents & CONTENTSQ3_CORPSE)
5368                 supercontents |= SUPERCONTENTS_CORPSE;
5369         if (nativecontents & CONTENTSQ3_NODROP)
5370                 supercontents |= SUPERCONTENTS_NODROP;
5371         if (nativecontents & CONTENTSQ3_PLAYERCLIP)
5372                 supercontents |= SUPERCONTENTS_PLAYERCLIP;
5373         if (nativecontents & CONTENTSQ3_MONSTERCLIP)
5374                 supercontents |= SUPERCONTENTS_MONSTERCLIP;
5375         if (nativecontents & CONTENTSQ3_DONOTENTER)
5376                 supercontents |= SUPERCONTENTS_DONOTENTER;
5377         return supercontents;
5378 }
5379
5380 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
5381 {
5382         int nativecontents = 0;
5383         if (supercontents & SUPERCONTENTS_SOLID)
5384                 nativecontents |= CONTENTSQ3_SOLID;
5385         if (supercontents & SUPERCONTENTS_WATER)
5386                 nativecontents |= CONTENTSQ3_WATER;
5387         if (supercontents & SUPERCONTENTS_SLIME)
5388                 nativecontents |= CONTENTSQ3_SLIME;
5389         if (supercontents & SUPERCONTENTS_LAVA)
5390                 nativecontents |= CONTENTSQ3_LAVA;
5391         if (supercontents & SUPERCONTENTS_BODY)
5392                 nativecontents |= CONTENTSQ3_BODY;
5393         if (supercontents & SUPERCONTENTS_CORPSE)
5394                 nativecontents |= CONTENTSQ3_CORPSE;
5395         if (supercontents & SUPERCONTENTS_NODROP)
5396                 nativecontents |= CONTENTSQ3_NODROP;
5397         if (supercontents & SUPERCONTENTS_PLAYERCLIP)
5398                 nativecontents |= CONTENTSQ3_PLAYERCLIP;
5399         if (supercontents & SUPERCONTENTS_MONSTERCLIP)
5400                 nativecontents |= CONTENTSQ3_MONSTERCLIP;
5401         if (supercontents & SUPERCONTENTS_DONOTENTER)
5402                 nativecontents |= CONTENTSQ3_DONOTENTER;
5403         return nativecontents;
5404 }
5405
5406 void Mod_Q3BSP_RecursiveFindNumLeafs(mnode_t *node)
5407 {
5408         int numleafs;
5409         while (node->plane)
5410         {
5411                 Mod_Q3BSP_RecursiveFindNumLeafs(node->children[0]);
5412                 node = node->children[1];
5413         }
5414         numleafs = ((mleaf_t *)node - loadmodel->brush.data_leafs) + 1;
5415         if (loadmodel->brush.num_leafs < numleafs)
5416                 loadmodel->brush.num_leafs = numleafs;
5417 }
5418
5419 void Mod_Q3BSP_Load(model_t *mod, void *buffer, void *bufferend)
5420 {
5421         int i, j, numshadowmeshtriangles;
5422         q3dheader_t *header;
5423         float corner[3], yawradius, modelradius;
5424         msurface_t *surface;
5425
5426         mod->type = mod_brushq3;
5427         mod->numframes = 2; // although alternate textures are not supported it is annoying to complain about no such frame 1
5428         mod->numskins = 1;
5429
5430         header = (q3dheader_t *)buffer;
5431
5432         i = LittleLong(header->version);
5433         if (i != Q3BSPVERSION)
5434                 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
5435         if (mod->isworldmodel)
5436                 Cvar_SetValue("halflifebsp", false);
5437
5438         mod->soundfromcenter = true;
5439         mod->TraceBox = Mod_Q3BSP_TraceBox;
5440         mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
5441         mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
5442         mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
5443         mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
5444         mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
5445         mod->brush.BoxTouchingLeafPVS = Mod_Q1BSP_BoxTouchingLeafPVS;
5446         mod->brush.BoxTouchingVisibleLeafs = Mod_Q1BSP_BoxTouchingVisibleLeafs;
5447         mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
5448         mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
5449         mod->brush.PointInLeaf = Mod_Q1BSP_PointInLeaf;
5450         mod->Draw = R_Q1BSP_Draw;
5451         mod->GetLightInfo = R_Q1BSP_GetLightInfo;
5452         mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
5453         mod->DrawLight = R_Q1BSP_DrawLight;
5454
5455         mod_base = (qbyte *)header;
5456
5457         // swap all the lumps
5458         header->ident = LittleLong(header->ident);
5459         header->version = LittleLong(header->version);
5460         for (i = 0;i < Q3HEADER_LUMPS;i++)
5461         {
5462                 header->lumps[i].fileofs = LittleLong(header->lumps[i].fileofs);
5463                 header->lumps[i].filelen = LittleLong(header->lumps[i].filelen);
5464         }
5465
5466         Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
5467         Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
5468         Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
5469         Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
5470         Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
5471         Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
5472         Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
5473         Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
5474         Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
5475         Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
5476         Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
5477         Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
5478         Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
5479         Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
5480         Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
5481         Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
5482         Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
5483         loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
5484
5485         // the MakePortals code works fine on the q3bsp data as well
5486         Mod_Q1BSP_MakePortals();
5487
5488         // make a single combined shadow mesh to allow optimized shadow volume creation
5489         numshadowmeshtriangles = 0;
5490         for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
5491         {
5492                 surface->num_firstshadowmeshtriangle = numshadowmeshtriangles;
5493                 numshadowmeshtriangles += surface->num_triangles;
5494         }
5495         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
5496         for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
5497                 if (surface->groupmesh)
5498                         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));
5499         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true);
5500         Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
5501
5502         loadmodel->brush.num_leafs = 0;
5503         Mod_Q3BSP_RecursiveFindNumLeafs(loadmodel->brush.data_nodes);
5504
5505         if (loadmodel->isworldmodel)
5506         {
5507                 // clear out any stale submodels or worldmodels lying around
5508                 // if we did this clear before now, an error might abort loading and
5509                 // leave things in a bad state
5510                 Mod_RemoveStaleWorldModels(loadmodel);
5511         }
5512
5513         mod = loadmodel;
5514         for (i = 0;i < loadmodel->brush.numsubmodels;i++)
5515         {
5516                 if (i > 0)
5517                 {
5518                         char name[10];
5519                         // LordHavoc: only register submodels if it is the world
5520                         // (prevents external bsp models from replacing world submodels with
5521                         //  their own)
5522                         if (!loadmodel->isworldmodel)
5523                                 continue;
5524                         // duplicate the basic information
5525                         sprintf(name, "*%i", i);
5526                         mod = Mod_FindName(name);
5527                         *mod = *loadmodel;
5528                         strcpy(mod->name, name);
5529                         // textures and memory belong to the main model
5530                         mod->texturepool = NULL;
5531                         mod->mempool = NULL;
5532                         mod->brush.GetPVS = NULL;
5533                         mod->brush.FatPVS = NULL;
5534                         mod->brush.BoxTouchingPVS = NULL;
5535                         mod->brush.BoxTouchingLeafPVS = NULL;
5536                         mod->brush.BoxTouchingVisibleLeafs = NULL;
5537                         mod->brush.LightPoint = NULL;
5538                         mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
5539                 }
5540                 mod->brush.submodel = i;
5541
5542                 // make the model surface list (used by shadowing/lighting)
5543                 mod->firstmodelsurface = mod->brushq3.data_models[i].firstface;
5544                 mod->nummodelsurfaces = mod->brushq3.data_models[i].numfaces;
5545                 mod->firstmodelbrush = mod->brushq3.data_models[i].firstbrush;
5546                 mod->nummodelbrushes = mod->brushq3.data_models[i].numbrushes;
5547                 mod->surfacelist = Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->surfacelist));
5548                 for (j = 0;j < mod->nummodelsurfaces;j++)
5549                         mod->surfacelist[j] = mod->firstmodelsurface + j;
5550
5551                 VectorCopy(mod->brushq3.data_models[i].mins, mod->normalmins);
5552                 VectorCopy(mod->brushq3.data_models[i].maxs, mod->normalmaxs);
5553                 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
5554                 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
5555                 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
5556                 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
5557                 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
5558                 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
5559                 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
5560                 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
5561                 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
5562                 mod->yawmins[2] = mod->normalmins[2];
5563                 mod->yawmaxs[2] = mod->normalmaxs[2];
5564                 mod->radius = modelradius;
5565                 mod->radius2 = modelradius * modelradius;
5566
5567                 for (j = 0;j < mod->nummodelsurfaces;j++)
5568                         if (mod->data_surfaces[j + mod->firstmodelsurface].texture->surfaceflags & Q3SURFACEFLAG_SKY)
5569                                 break;
5570                 if (j < mod->nummodelsurfaces)
5571                         mod->DrawSky = R_Q1BSP_DrawSky;
5572         }
5573 }
5574
5575 void Mod_IBSP_Load(model_t *mod, void *buffer, void *bufferend)
5576 {
5577         int i = LittleLong(((int *)buffer)[1]);
5578         if (i == Q3BSPVERSION)
5579                 Mod_Q3BSP_Load(mod,buffer, bufferend);
5580         else if (i == Q2BSPVERSION)
5581                 Mod_Q2BSP_Load(mod,buffer, bufferend);
5582         else
5583                 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
5584 }
5585
5586 void Mod_MAP_Load(model_t *mod, void *buffer, void *bufferend)
5587 {
5588         Host_Error("Mod_MAP_Load: not yet implemented\n");
5589 }
5590