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