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