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