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