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