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