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