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