]> icculus.org git repositories - divverent/darkplaces.git/blob - model_brush.c
moved brushq3.submodel to brush, removed brushq3.data_thismodel (instead looking...
[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                 mod->brush.submodel = i;
3108
3109                 if (loadmodel->brush.submodels)
3110                         loadmodel->brush.submodels[i] = mod;
3111
3112                 bm = &mod->brushq1.submodels[i];
3113
3114                 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
3115                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
3116                 {
3117                         mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
3118                         mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
3119                 }
3120
3121                 mod->firstmodelsurface = bm->firstface;
3122                 mod->nummodelsurfaces = bm->numfaces;
3123
3124                 // make the model surface list (used by shadowing/lighting)
3125                 mod->surfacelist = Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->surfacelist));
3126                 for (j = 0;j < mod->nummodelsurfaces;j++)
3127                         mod->surfacelist[j] = mod->firstmodelsurface + j;
3128
3129                 // this gets altered below if sky is used
3130                 mod->DrawSky = NULL;
3131                 mod->Draw = R_Q1BSP_Draw;
3132                 mod->GetLightInfo = R_Q1BSP_GetLightInfo;
3133                 mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
3134                 mod->DrawLight = R_Q1BSP_DrawLight;
3135                 if (i != 0)
3136                 {
3137                         mod->brush.GetPVS = NULL;
3138                         mod->brush.FatPVS = NULL;
3139                         mod->brush.BoxTouchingPVS = NULL;
3140                         mod->brush.LightPoint = NULL;
3141                         mod->brush.AmbientSoundLevelsForPoint = NULL;
3142                 }
3143                 Mod_Q1BSP_BuildLightmapUpdateChains(loadmodel->mempool, mod);
3144                 if (mod->nummodelsurfaces)
3145                 {
3146                         // LordHavoc: calculate bmodel bounding box rather than trusting what it says
3147                         mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
3148                         mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
3149                         modelyawradius = 0;
3150                         modelradius = 0;
3151                         for (j = 0, surf = &mod->brushq1.surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
3152                         {
3153                                 // we only need to have a drawsky function if it is used(usually only on world model)
3154                                 if (surf->texinfo->texture->flags & SURF_DRAWSKY)
3155                                         mod->DrawSky = R_Q1BSP_DrawSky;
3156                                 // LordHavoc: submodels always clip, even if water
3157                                 if (mod->brush.numsubmodels - 1)
3158                                         surf->flags |= SURF_SOLIDCLIP;
3159                                 // calculate bounding shapes
3160                                 for (k = 0, vec = surf->mesh.data_vertex3f;k < surf->mesh.num_vertices;k++, vec += 3)
3161                                 {
3162                                         if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
3163                                         if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
3164                                         if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
3165                                         if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
3166                                         if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
3167                                         if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
3168                                         dist = vec[0]*vec[0]+vec[1]*vec[1];
3169                                         if (modelyawradius < dist)
3170                                                 modelyawradius = dist;
3171                                         dist += vec[2]*vec[2];
3172                                         if (modelradius < dist)
3173                                                 modelradius = dist;
3174                                 }
3175                         }
3176                         modelyawradius = sqrt(modelyawradius);
3177                         modelradius = sqrt(modelradius);
3178                         mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
3179                         mod->yawmins[2] = mod->normalmins[2];
3180                         mod->yawmaxs[2] = mod->normalmaxs[2];
3181                         mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
3182                         mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
3183                         mod->radius = modelradius;
3184                         mod->radius2 = modelradius * modelradius;
3185                 }
3186                 else
3187                 {
3188                         // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
3189                         Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadmodel->name);
3190                 }
3191                 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->firstmodelsurface, mod->nummodelsurfaces, loadmodel->mempool);
3192
3193                 //mod->brushq1.num_visleafs = bm->visleafs;
3194         }
3195
3196         Mod_Q1BSP_LoadMapBrushes();
3197
3198         //Mod_Q1BSP_ProcessLightList();
3199
3200         if (developer.integer)
3201                 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);
3202 }
3203
3204 static void Mod_Q2BSP_LoadEntities(lump_t *l)
3205 {
3206 }
3207
3208 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
3209 {
3210 /*
3211         d_t *in;
3212         m_t *out;
3213         int i, count;
3214
3215         in = (void *)(mod_base + l->fileofs);
3216         if (l->filelen % sizeof(*in))
3217                 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3218         count = l->filelen / sizeof(*in);
3219         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3220
3221         loadmodel-> = out;
3222         loadmodel->num = count;
3223
3224         for (i = 0;i < count;i++, in++, out++)
3225         {
3226         }
3227 */
3228 }
3229
3230 static void Mod_Q2BSP_LoadVertices(lump_t *l)
3231 {
3232 /*
3233         d_t *in;
3234         m_t *out;
3235         int i, count;
3236
3237         in = (void *)(mod_base + l->fileofs);
3238         if (l->filelen % sizeof(*in))
3239                 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3240         count = l->filelen / sizeof(*in);
3241         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3242
3243         loadmodel-> = out;
3244         loadmodel->num = count;
3245
3246         for (i = 0;i < count;i++, in++, out++)
3247         {
3248         }
3249 */
3250 }
3251
3252 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
3253 {
3254 /*
3255         d_t *in;
3256         m_t *out;
3257         int i, count;
3258
3259         in = (void *)(mod_base + l->fileofs);
3260         if (l->filelen % sizeof(*in))
3261                 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
3262         count = l->filelen / sizeof(*in);
3263         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3264
3265         loadmodel-> = out;
3266         loadmodel->num = count;
3267
3268         for (i = 0;i < count;i++, in++, out++)
3269         {
3270         }
3271 */
3272 }
3273
3274 static void Mod_Q2BSP_LoadNodes(lump_t *l)
3275 {
3276 /*
3277         d_t *in;
3278         m_t *out;
3279         int i, count;
3280
3281         in = (void *)(mod_base + l->fileofs);
3282         if (l->filelen % sizeof(*in))
3283                 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3284         count = l->filelen / sizeof(*in);
3285         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3286
3287         loadmodel-> = out;
3288         loadmodel->num = count;
3289
3290         for (i = 0;i < count;i++, in++, out++)
3291         {
3292         }
3293 */
3294 }
3295
3296 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
3297 {
3298 /*
3299         d_t *in;
3300         m_t *out;
3301         int i, count;
3302
3303         in = (void *)(mod_base + l->fileofs);
3304         if (l->filelen % sizeof(*in))
3305                 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
3306         count = l->filelen / sizeof(*in);
3307         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3308
3309         loadmodel-> = out;
3310         loadmodel->num = count;
3311
3312         for (i = 0;i < count;i++, in++, out++)
3313         {
3314         }
3315 */
3316 }
3317
3318 static void Mod_Q2BSP_LoadFaces(lump_t *l)
3319 {
3320 /*
3321         d_t *in;
3322         m_t *out;
3323         int i, count;
3324
3325         in = (void *)(mod_base + l->fileofs);
3326         if (l->filelen % sizeof(*in))
3327                 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3328         count = l->filelen / sizeof(*in);
3329         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3330
3331         loadmodel-> = out;
3332         loadmodel->num = count;
3333
3334         for (i = 0;i < count;i++, in++, out++)
3335         {
3336         }
3337 */
3338 }
3339
3340 static void Mod_Q2BSP_LoadLighting(lump_t *l)
3341 {
3342 /*
3343         d_t *in;
3344         m_t *out;
3345         int i, count;
3346
3347         in = (void *)(mod_base + l->fileofs);
3348         if (l->filelen % sizeof(*in))
3349                 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
3350         count = l->filelen / sizeof(*in);
3351         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3352
3353         loadmodel-> = out;
3354         loadmodel->num = count;
3355
3356         for (i = 0;i < count;i++, in++, out++)
3357         {
3358         }
3359 */
3360 }
3361
3362 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
3363 {
3364 /*
3365         d_t *in;
3366         m_t *out;
3367         int i, count;
3368
3369         in = (void *)(mod_base + l->fileofs);
3370         if (l->filelen % sizeof(*in))
3371                 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3372         count = l->filelen / sizeof(*in);
3373         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3374
3375         loadmodel-> = out;
3376         loadmodel->num = count;
3377
3378         for (i = 0;i < count;i++, in++, out++)
3379         {
3380         }
3381 */
3382 }
3383
3384 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3385 {
3386 /*
3387         d_t *in;
3388         m_t *out;
3389         int i, count;
3390
3391         in = (void *)(mod_base + l->fileofs);
3392         if (l->filelen % sizeof(*in))
3393                 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3394         count = l->filelen / sizeof(*in);
3395         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3396
3397         loadmodel-> = out;
3398         loadmodel->num = count;
3399
3400         for (i = 0;i < count;i++, in++, out++)
3401         {
3402         }
3403 */
3404 }
3405
3406 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3407 {
3408 /*
3409         d_t *in;
3410         m_t *out;
3411         int i, count;
3412
3413         in = (void *)(mod_base + l->fileofs);
3414         if (l->filelen % sizeof(*in))
3415                 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3416         count = l->filelen / sizeof(*in);
3417         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3418
3419         loadmodel-> = out;
3420         loadmodel->num = count;
3421
3422         for (i = 0;i < count;i++, in++, out++)
3423         {
3424         }
3425 */
3426 }
3427
3428 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3429 {
3430 /*
3431         d_t *in;
3432         m_t *out;
3433         int i, count;
3434
3435         in = (void *)(mod_base + l->fileofs);
3436         if (l->filelen % sizeof(*in))
3437                 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3438         count = l->filelen / sizeof(*in);
3439         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3440
3441         loadmodel-> = out;
3442         loadmodel->num = count;
3443
3444         for (i = 0;i < count;i++, in++, out++)
3445         {
3446         }
3447 */
3448 }
3449
3450 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3451 {
3452 /*
3453         d_t *in;
3454         m_t *out;
3455         int i, count;
3456
3457         in = (void *)(mod_base + l->fileofs);
3458         if (l->filelen % sizeof(*in))
3459                 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3460         count = l->filelen / sizeof(*in);
3461         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3462
3463         loadmodel-> = out;
3464         loadmodel->num = count;
3465
3466         for (i = 0;i < count;i++, in++, out++)
3467         {
3468         }
3469 */
3470 }
3471
3472 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3473 {
3474 /*
3475         d_t *in;
3476         m_t *out;
3477         int i, count;
3478
3479         in = (void *)(mod_base + l->fileofs);
3480         if (l->filelen % sizeof(*in))
3481                 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3482         count = l->filelen / sizeof(*in);
3483         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3484
3485         loadmodel-> = out;
3486         loadmodel->num = count;
3487
3488         for (i = 0;i < count;i++, in++, out++)
3489         {
3490         }
3491 */
3492 }
3493
3494 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3495 {
3496 /*
3497         d_t *in;
3498         m_t *out;
3499         int i, count;
3500
3501         in = (void *)(mod_base + l->fileofs);
3502         if (l->filelen % sizeof(*in))
3503                 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3504         count = l->filelen / sizeof(*in);
3505         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3506
3507         loadmodel-> = out;
3508         loadmodel->num = count;
3509
3510         for (i = 0;i < count;i++, in++, out++)
3511         {
3512         }
3513 */
3514 }
3515
3516 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3517 {
3518 /*
3519         d_t *in;
3520         m_t *out;
3521         int i, count;
3522
3523         in = (void *)(mod_base + l->fileofs);
3524         if (l->filelen % sizeof(*in))
3525                 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3526         count = l->filelen / sizeof(*in);
3527         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3528
3529         loadmodel-> = out;
3530         loadmodel->num = count;
3531
3532         for (i = 0;i < count;i++, in++, out++)
3533         {
3534         }
3535 */
3536 }
3537
3538 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3539 {
3540 /*
3541         d_t *in;
3542         m_t *out;
3543         int i, count;
3544
3545         in = (void *)(mod_base + l->fileofs);
3546         if (l->filelen % sizeof(*in))
3547                 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3548         count = l->filelen / sizeof(*in);
3549         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3550
3551         loadmodel-> = out;
3552         loadmodel->num = count;
3553
3554         for (i = 0;i < count;i++, in++, out++)
3555         {
3556         }
3557 */
3558 }
3559
3560 static void Mod_Q2BSP_LoadModels(lump_t *l)
3561 {
3562 /*
3563         d_t *in;
3564         m_t *out;
3565         int i, count;
3566
3567         in = (void *)(mod_base + l->fileofs);
3568         if (l->filelen % sizeof(*in))
3569                 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3570         count = l->filelen / sizeof(*in);
3571         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3572
3573         loadmodel-> = out;
3574         loadmodel->num = count;
3575
3576         for (i = 0;i < count;i++, in++, out++)
3577         {
3578         }
3579 */
3580 }
3581
3582 void static Mod_Q2BSP_Load(model_t *mod, void *buffer)
3583 {
3584         int i;
3585         q2dheader_t *header;
3586
3587         Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3588
3589         mod->type = mod_brushq2;
3590
3591         header = (q2dheader_t *)buffer;
3592
3593         i = LittleLong(header->version);
3594         if (i != Q2BSPVERSION)
3595                 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3596         mod->brush.ishlbsp = false;
3597         if (loadmodel->isworldmodel)
3598                 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3599
3600         mod_base = (qbyte *)header;
3601
3602         // swap all the lumps
3603         for (i = 0;i < (int) sizeof(*header) / 4;i++)
3604                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3605
3606         // store which lightmap format to use
3607         mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3608
3609         Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3610         Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3611         Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3612         Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3613         Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3614         Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3615         Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3616         Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3617         Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3618         Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3619         Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3620         Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3621         Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3622         Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3623         Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3624         Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3625         Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3626         // LordHavoc: must go last because this makes the submodels
3627         Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3628 }
3629
3630 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents);
3631 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents);
3632
3633 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3634 {
3635         const char *data;
3636         char key[128], value[4096];
3637         float v[3];
3638         loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3639         loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3640         loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3641         if (!l->filelen)
3642                 return;
3643         loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3644         memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3645         data = loadmodel->brush.entities;
3646         // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3647         if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3648         {
3649                 while (1)
3650                 {
3651                         if (!COM_ParseToken(&data, false))
3652                                 break; // error
3653                         if (com_token[0] == '}')
3654                                 break; // end of worldspawn
3655                         if (com_token[0] == '_')
3656                                 strcpy(key, com_token + 1);
3657                         else
3658                                 strcpy(key, com_token);
3659                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
3660                                 key[strlen(key)-1] = 0;
3661                         if (!COM_ParseToken(&data, false))
3662                                 break; // error
3663                         strcpy(value, com_token);
3664                         if (!strcmp("gridsize", key))
3665                         {
3666                                 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3667                                         VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3668                         }
3669                 }
3670         }
3671 }
3672
3673 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3674 {
3675         q3dtexture_t *in;
3676         q3mtexture_t *out;
3677         int i, count;
3678         int j, c;
3679         fssearch_t *search;
3680         char *f;
3681         const char *text;
3682         int flags, flags2, numparameters, passnumber;
3683         char shadername[Q3PATHLENGTH];
3684         char sky[Q3PATHLENGTH];
3685         char firstpasstexturename[Q3PATHLENGTH];
3686         char parameter[4][Q3PATHLENGTH];
3687
3688         in = (void *)(mod_base + l->fileofs);
3689         if (l->filelen % sizeof(*in))
3690                 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3691         count = l->filelen / sizeof(*in);
3692         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3693
3694         loadmodel->brushq3.data_textures = out;
3695         loadmodel->brushq3.num_textures = count;
3696
3697         for (i = 0;i < count;i++, in++, out++)
3698         {
3699                 out->number = i;
3700                 strlcpy (out->name, in->name, sizeof (out->name));
3701                 out->surfaceflags = LittleLong(in->surfaceflags);
3702                 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, LittleLong(in->contents));
3703                 out->surfaceparms = -1;
3704         }
3705
3706         // do a quick parse of shader files to get surfaceparms
3707         if ((search = FS_Search("scripts/*.shader", true, false)))
3708         {
3709                 for (i = 0;i < search->numfilenames;i++)
3710                 {
3711                         if ((f = FS_LoadFile(search->filenames[i], tempmempool, false)))
3712                         {
3713                                 text = f;
3714                                 while (COM_ParseToken(&text, false))
3715                                 {
3716                                         strlcpy (shadername, com_token, sizeof (shadername));
3717                                         flags = 0;
3718                                         flags2 = 0;
3719                                         sky[0] = 0;
3720                                         passnumber = 0;
3721                                         firstpasstexturename[0] = 0;
3722                                         if (COM_ParseToken(&text, false) && !strcasecmp(com_token, "{"))
3723                                         {
3724                                                 while (COM_ParseToken(&text, false))
3725                                                 {
3726                                                         if (!strcasecmp(com_token, "}"))
3727                                                                 break;
3728                                                         else if (!strcasecmp(com_token, "{"))
3729                                                         {
3730                                                                 while (COM_ParseToken(&text, false))
3731                                                                 {
3732                                                                         if (!strcasecmp(com_token, "}"))
3733                                                                                 break;
3734                                                                         if (!strcasecmp(com_token, "\n"))
3735                                                                                 continue;
3736                                                                         numparameters = 0;
3737                                                                         for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
3738                                                                         {
3739                                                                                 if (j < 4)
3740                                                                                 {
3741                                                                                         strlcpy(parameter[j], com_token, sizeof(parameter[j]));
3742                                                                                         numparameters = j + 1;
3743                                                                                 }
3744                                                                                 if (!COM_ParseToken(&text, true))
3745                                                                                         break;
3746                                                                         }
3747                                                                         if (developer.integer >= 2)
3748                                                                         {
3749                                                                                 Con_Printf("%s %i: ", shadername, passnumber);
3750                                                                                 for (j = 0;j < numparameters;j++)
3751                                                                                         Con_Printf(" %s", parameter[j]);
3752                                                                                 Con_Print("\n");
3753                                                                         }
3754                                                                         if (passnumber == 0 && numparameters >= 1)
3755                                                                         {
3756                                                                                 if (!strcasecmp(parameter[0], "blendfunc"))
3757                                                                                 {
3758                                                                                         if (numparameters == 2 && !strcasecmp(parameter[1], "add"))
3759                                                                                                 flags2 |= Q3TEXTUREFLAG_ADDITIVE;
3760                                                                                         else if (numparameters == 3 && !strcasecmp(parameter[1], "gl_one") && !strcasecmp(parameter[2], "gl_one"))
3761                                                                                                 flags2 |= Q3TEXTUREFLAG_ADDITIVE;
3762                                                                                         else if (numparameters == 3 && !strcasecmp(parameter[1], "gl_src_alpha") && !strcasecmp(parameter[2], "gl_one"))
3763                                                                                                 flags2 |= Q3TEXTUREFLAG_ADDITIVE;
3764                                                                                 }
3765                                                                                 else if (numparameters >= 2 && (!strcasecmp(parameter[0], "map") || !strcasecmp(parameter[0], "clampmap")))
3766                                                                                         strlcpy(firstpasstexturename, parameter[1], sizeof(firstpasstexturename));
3767                                                                                 else if (numparameters >= 3 && !strcasecmp(parameter[0], "animmap"))
3768                                                                                         strlcpy(firstpasstexturename, parameter[2], sizeof(firstpasstexturename));
3769                                                                         }
3770                                                                         if (!strcasecmp(parameter[0], "alphafunc"))
3771                                                                                 flags2 |= Q3TEXTUREFLAG_ALPHATEST;
3772                                                                         // break out a level if it was }
3773                                                                         if (!strcasecmp(com_token, "}"))
3774                                                                                 break;
3775                                                                 }
3776                                                                 passnumber++;
3777                                                                 continue;
3778                                                         }
3779                                                         numparameters = 0;
3780                                                         for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
3781                                                         {
3782                                                                 if (j < 4)
3783                                                                 {
3784                                                                         strlcpy(parameter[j], com_token, sizeof(parameter[j]));
3785                                                                         numparameters = j + 1;
3786                                                                 }
3787                                                                 if (!COM_ParseToken(&text, true))
3788                                                                         break;
3789                                                         }
3790                                                         if (i == 0 && !strcasecmp(com_token, "}"))
3791                                                                 break;
3792                                                         if (developer.integer >= 2)
3793                                                         {
3794                                                                 Con_Printf("%s: ", shadername);
3795                                                                 for (j = 0;j < numparameters;j++)
3796                                                                         Con_Printf(" %s", parameter[j]);
3797                                                                 Con_Print("\n");
3798                                                         }
3799                                                         if (numparameters < 1)
3800                                                                 continue;
3801                                                         if (!strcasecmp(parameter[0], "surfaceparm") && numparameters >= 2)
3802                                                         {
3803                                                                 if (!strcasecmp(parameter[1], "alphashadow"))
3804                                                                         flags |= Q3SURFACEPARM_ALPHASHADOW;
3805                                                                 else if (!strcasecmp(parameter[1], "areaportal"))
3806                                                                         flags |= Q3SURFACEPARM_AREAPORTAL;
3807                                                                 else if (!strcasecmp(parameter[1], "clusterportal"))
3808                                                                         flags |= Q3SURFACEPARM_CLUSTERPORTAL;
3809                                                                 else if (!strcasecmp(parameter[1], "detail"))
3810                                                                         flags |= Q3SURFACEPARM_DETAIL;
3811                                                                 else if (!strcasecmp(parameter[1], "donotenter"))
3812                                                                         flags |= Q3SURFACEPARM_DONOTENTER;
3813                                                                 else if (!strcasecmp(parameter[1], "fog"))
3814                                                                         flags |= Q3SURFACEPARM_FOG;
3815                                                                 else if (!strcasecmp(parameter[1], "lava"))
3816                                                                         flags |= Q3SURFACEPARM_LAVA;
3817                                                                 else if (!strcasecmp(parameter[1], "lightfilter"))
3818                                                                         flags |= Q3SURFACEPARM_LIGHTFILTER;
3819                                                                 else if (!strcasecmp(parameter[1], "metalsteps"))
3820                                                                         flags |= Q3SURFACEPARM_METALSTEPS;
3821                                                                 else if (!strcasecmp(parameter[1], "nodamage"))
3822                                                                         flags |= Q3SURFACEPARM_NODAMAGE;
3823                                                                 else if (!strcasecmp(parameter[1], "nodlight"))
3824                                                                         flags |= Q3SURFACEPARM_NODLIGHT;
3825                                                                 else if (!strcasecmp(parameter[1], "nodraw"))
3826                                                                         flags |= Q3SURFACEPARM_NODRAW;
3827                                                                 else if (!strcasecmp(parameter[1], "nodrop"))
3828                                                                         flags |= Q3SURFACEPARM_NODROP;
3829                                                                 else if (!strcasecmp(parameter[1], "noimpact"))
3830                                                                         flags |= Q3SURFACEPARM_NOIMPACT;
3831                                                                 else if (!strcasecmp(parameter[1], "nolightmap"))
3832                                                                         flags |= Q3SURFACEPARM_NOLIGHTMAP;
3833                                                                 else if (!strcasecmp(parameter[1], "nomarks"))
3834                                                                         flags |= Q3SURFACEPARM_NOMARKS;
3835                                                                 else if (!strcasecmp(parameter[1], "nomipmaps"))
3836                                                                         flags |= Q3SURFACEPARM_NOMIPMAPS;
3837                                                                 else if (!strcasecmp(parameter[1], "nonsolid"))
3838                                                                         flags |= Q3SURFACEPARM_NONSOLID;
3839                                                                 else if (!strcasecmp(parameter[1], "origin"))
3840                                                                         flags |= Q3SURFACEPARM_ORIGIN;
3841                                                                 else if (!strcasecmp(parameter[1], "playerclip"))
3842                                                                         flags |= Q3SURFACEPARM_PLAYERCLIP;
3843                                                                 else if (!strcasecmp(parameter[1], "sky"))
3844                                                                         flags |= Q3SURFACEPARM_SKY;
3845                                                                 else if (!strcasecmp(parameter[1], "slick"))
3846                                                                         flags |= Q3SURFACEPARM_SLICK;
3847                                                                 else if (!strcasecmp(parameter[1], "slime"))
3848                                                                         flags |= Q3SURFACEPARM_SLIME;
3849                                                                 else if (!strcasecmp(parameter[1], "structural"))
3850                                                                         flags |= Q3SURFACEPARM_STRUCTURAL;
3851                                                                 else if (!strcasecmp(parameter[1], "trans"))
3852                                                                         flags |= Q3SURFACEPARM_TRANS;
3853                                                                 else if (!strcasecmp(parameter[1], "water"))
3854                                                                         flags |= Q3SURFACEPARM_WATER;
3855                                                                 else if (!strcasecmp(parameter[1], "pointlight"))
3856                                                                         flags |= Q3SURFACEPARM_POINTLIGHT;
3857                                                                 else
3858                                                                         Con_Printf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[i], parameter[1]);
3859                                                         }
3860                                                         else if (!strcasecmp(parameter[0], "sky") && numparameters >= 2)
3861                                                                 strlcpy(sky, parameter[1], sizeof(sky));
3862                                                         else if (!strcasecmp(parameter[0], "skyparms") && numparameters >= 2)
3863                                                         {
3864                                                                 if (!atoi(parameter[1]) && strcasecmp(parameter[1], "-"))
3865                                                                         strlcpy(sky, parameter[1], sizeof(sky));
3866                                                         }
3867                                                         else if (!strcasecmp(parameter[0], "cull") && numparameters >= 2)
3868                                                         {
3869                                                                 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "twosided"))
3870                                                                         flags2 |= Q3TEXTUREFLAG_TWOSIDED;
3871                                                         }
3872                                                         else if (!strcasecmp(parameter[0], "nomipmaps"))
3873                                                                 flags2 |= Q3TEXTUREFLAG_NOMIPMAPS;
3874                                                         else if (!strcasecmp(parameter[0], "nopicmip"))
3875                                                                 flags2 |= Q3TEXTUREFLAG_NOPICMIP;
3876                                                         else if (!strcasecmp(parameter[0], "deformvertexes") && numparameters >= 2)
3877                                                         {
3878                                                                 if (!strcasecmp(parameter[1], "autosprite") && numparameters == 2)
3879                                                                         flags2 |= Q3TEXTUREFLAG_AUTOSPRITE;
3880                                                                 if (!strcasecmp(parameter[1], "autosprite2") && numparameters == 2)
3881                                                                         flags2 |= Q3TEXTUREFLAG_AUTOSPRITE2;
3882                                                         }
3883                                                 }
3884                                                 // force transparent render path for a number of odd
3885                                                 // shader effects to avoid bogging down the normal
3886                                                 // render path unnecessarily
3887                                                 if (flags2 & (Q3TEXTUREFLAG_ADDITIVE | Q3TEXTUREFLAG_AUTOSPRITE | Q3TEXTUREFLAG_AUTOSPRITE2 | Q3TEXTUREFLAG_ALPHATEST))
3888                                                         flags |= Q3SURFACEPARM_TRANS;
3889                                                 // add shader to list (shadername and flags)
3890                                                 // actually here we just poke into the texture settings
3891                                                 for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++)
3892                                                 {
3893                                                         if (!strcasecmp(out->name, shadername))
3894                                                         {
3895                                                                 out->surfaceparms = flags;
3896                                                                 out->textureflags = flags2;
3897                                                                 strlcpy(out->firstpasstexturename, firstpasstexturename, sizeof(out->firstpasstexturename));
3898                                                                 if ((flags & Q3SURFACEPARM_SKY) && sky[0])
3899                                                                 {
3900                                                                         // quake3 seems to append a _ to the skybox name, so this must do so as well
3901                                                                         dpsnprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", sky);
3902                                                                 }
3903                                                         }
3904                                                 }
3905                                         }
3906                                         else
3907                                         {
3908                                                 Con_Printf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[i], com_token);
3909                                                 goto parseerror;
3910                                         }
3911                                 }
3912 parseerror:
3913                                 Mem_Free(f);
3914                         }
3915                 }
3916         }
3917
3918         c = 0;
3919         for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++)
3920         {
3921                 if (out->surfaceparms == -1)
3922                 {
3923                         c++;
3924                         Con_DPrintf("%s: No shader found for texture \"%s\"\n", loadmodel->name, out->name);
3925                         out->surfaceparms = 0;
3926                         // these are defaults
3927                         //if (!strncmp(out->name, "textures/skies/", 15))
3928                         //      out->surfaceparms |= Q3SURFACEPARM_SKY;
3929                         //if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk")
3930                         // || !strcmp(out->name, "nodraw") || !strcmp(out->name, "common/nodraw") || !strcmp(out->name, "textures/common/nodraw"))
3931                         //      out->surfaceparms |= Q3SURFACEPARM_NODRAW;
3932                         //if (R_TextureHasAlpha(out->skin.base))
3933                         //      out->surfaceparms |= Q3SURFACEPARM_TRANS;
3934                 }
3935                 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))
3936                         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))
3937                                 Con_Printf("%s: texture loading for shader \"%s\" failed (first layer \"%s\" not found either)\n", loadmodel->name, out->name, out->firstpasstexturename);
3938         }
3939         if (c)
3940                 Con_DPrintf("%s: %i textures missing shaders\n", loadmodel->name, c);
3941 }
3942
3943 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3944 {
3945         q3dplane_t *in;
3946         mplane_t *out;
3947         int i, count;
3948
3949         in = (void *)(mod_base + l->fileofs);
3950         if (l->filelen % sizeof(*in))
3951                 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3952         count = l->filelen / sizeof(*in);
3953         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3954
3955         loadmodel->brush.data_planes = out;
3956         loadmodel->brush.num_planes = count;
3957
3958         for (i = 0;i < count;i++, in++, out++)
3959         {
3960                 out->normal[0] = LittleLong(in->normal[0]);
3961                 out->normal[1] = LittleLong(in->normal[1]);
3962                 out->normal[2] = LittleLong(in->normal[2]);
3963                 out->dist = LittleLong(in->dist);
3964                 PlaneClassify(out);
3965         }
3966 }
3967
3968 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3969 {
3970         q3dbrushside_t *in;
3971         q3mbrushside_t *out;
3972         int i, n, count;
3973
3974         in = (void *)(mod_base + l->fileofs);
3975         if (l->filelen % sizeof(*in))
3976                 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3977         count = l->filelen / sizeof(*in);
3978         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3979
3980         loadmodel->brushq3.data_brushsides = out;
3981         loadmodel->brushq3.num_brushsides = count;
3982
3983         for (i = 0;i < count;i++, in++, out++)
3984         {
3985                 n = LittleLong(in->planeindex);
3986                 if (n < 0 || n >= loadmodel->brush.num_planes)
3987                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brush.num_planes);
3988                 out->plane = loadmodel->brush.data_planes + n;
3989                 n = LittleLong(in->textureindex);
3990                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3991                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3992                 out->texture = loadmodel->brushq3.data_textures + n;
3993         }
3994 }
3995
3996 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3997 {
3998         q3dbrush_t *in;
3999         q3mbrush_t *out;
4000         int i, j, n, c, count, maxplanes;
4001         mplane_t *planes;
4002
4003         in = (void *)(mod_base + l->fileofs);
4004         if (l->filelen % sizeof(*in))
4005                 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
4006         count = l->filelen / sizeof(*in);
4007         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4008
4009         loadmodel->brushq3.data_brushes = out;
4010         loadmodel->brushq3.num_brushes = count;
4011
4012         maxplanes = 0;
4013         planes = NULL;
4014
4015         for (i = 0;i < count;i++, in++, out++)
4016         {
4017                 n = LittleLong(in->firstbrushside);
4018                 c = LittleLong(in->numbrushsides);
4019                 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
4020                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
4021                 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
4022                 out->numbrushsides = c;
4023                 n = LittleLong(in->textureindex);
4024                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
4025                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
4026                 out->texture = loadmodel->brushq3.data_textures + n;
4027
4028                 // make a list of mplane_t structs to construct a colbrush from
4029                 if (maxplanes < out->numbrushsides)
4030                 {
4031                         maxplanes = out->numbrushsides;
4032                         if (planes)
4033                                 Mem_Free(planes);
4034                         planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
4035                 }
4036                 for (j = 0;j < out->numbrushsides;j++)
4037                 {
4038                         VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
4039                         planes[j].dist = out->firstbrushside[j].plane->dist;
4040                 }
4041                 // make the colbrush from the planes
4042                 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents);
4043         }
4044         if (planes)
4045                 Mem_Free(planes);
4046 }
4047
4048 static void Mod_Q3BSP_LoadEffects(lump_t *l)
4049 {
4050         q3deffect_t *in;
4051         q3meffect_t *out;
4052         int i, n, count;
4053
4054         in = (void *)(mod_base + l->fileofs);
4055         if (l->filelen % sizeof(*in))
4056                 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
4057         count = l->filelen / sizeof(*in);
4058         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4059
4060         loadmodel->brushq3.data_effects = out;
4061         loadmodel->brushq3.num_effects = count;
4062
4063         for (i = 0;i < count;i++, in++, out++)
4064         {
4065                 strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
4066                 n = LittleLong(in->brushindex);
4067                 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
4068                         Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
4069                 out->brush = loadmodel->brushq3.data_brushes + n;
4070                 out->unknown = LittleLong(in->unknown);
4071         }
4072 }
4073
4074 static void Mod_Q3BSP_LoadVertices(lump_t *l)
4075 {
4076         q3dvertex_t *in;
4077         int i, count;
4078
4079         in = (void *)(mod_base + l->fileofs);
4080         if (l->filelen % sizeof(*in))
4081                 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
4082         loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
4083         loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * (sizeof(float) * (3 + 2 + 2 + 4)));
4084         loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + count * 3;
4085         loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + count * 2;
4086         loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_texcoordlightmap2f + count * 2;
4087
4088         for (i = 0;i < count;i++, in++)
4089         {
4090                 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
4091                 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
4092                 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
4093                 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
4094                 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
4095                 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
4096                 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
4097                 // svector/tvector are calculated later in face loading
4098                 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
4099                 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
4100                 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
4101                 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
4102         }
4103 }
4104
4105 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
4106 {
4107         int *in;
4108         int *out;
4109         int i, count;
4110
4111         in = (void *)(mod_base + l->fileofs);
4112         if (l->filelen % sizeof(int[3]))
4113                 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
4114         count = l->filelen / sizeof(*in);
4115         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4116
4117         loadmodel->brushq3.num_triangles = count / 3;
4118         loadmodel->brushq3.data_element3i = out;
4119
4120         for (i = 0;i < count;i++, in++, out++)
4121         {
4122                 *out = LittleLong(*in);
4123                 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
4124                 {
4125                         Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
4126                         *out = 0;
4127                 }
4128         }
4129 }
4130
4131 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
4132 {
4133         q3dlightmap_t *in;
4134         rtexture_t **out;
4135         int i, count;
4136
4137         if (!l->filelen)
4138                 return;
4139         in = (void *)(mod_base + l->fileofs);
4140         if (l->filelen % sizeof(*in))
4141                 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
4142         count = l->filelen / sizeof(*in);
4143         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4144
4145         loadmodel->brushq3.data_lightmaps = out;
4146         loadmodel->brushq3.num_lightmaps = count;
4147
4148         for (i = 0;i < count;i++, in++, out++)
4149                 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
4150 }
4151
4152 static void Mod_Q3BSP_LoadFaces(lump_t *l)
4153 {
4154         q3dface_t *in, *oldin;
4155         q3msurface_t *out, *oldout;
4156         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;
4157         //int *originalelement3i;
4158         //int *originalneighbor3i;
4159         float *originalvertex3f;
4160         //float *originalsvector3f;
4161         //float *originaltvector3f;
4162         //float *originalnormal3f;
4163         float *originalcolor4f;
4164         float *originaltexcoordtexture2f;
4165         float *originaltexcoordlightmap2f;
4166         float *v;
4167         surfmesh_t *mesh, *tempmeshlist[1024];
4168
4169         in = (void *)(mod_base + l->fileofs);
4170         if (l->filelen % sizeof(*in))
4171                 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
4172         count = l->filelen / sizeof(*in);
4173         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4174
4175         loadmodel->brushq3.data_faces = out;
4176         loadmodel->brushq3.num_faces = count;
4177
4178         i = 0;
4179         for (meshnum = 0;i < count;meshnum++)
4180         {
4181                 oldi = i;
4182                 oldin = in;
4183                 oldout = out;
4184                 meshvertices = 0;
4185                 meshtriangles = 0;
4186                 for (;i < count;i++, in++, out++)
4187                 {
4188                         // check face type first
4189                         type = LittleLong(in->type);
4190                         if (type != Q3FACETYPE_POLYGON
4191                          && type != Q3FACETYPE_PATCH
4192                          && type != Q3FACETYPE_MESH
4193                          && type != Q3FACETYPE_FLARE)
4194                         {
4195                                 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, type);
4196                                 continue;
4197                         }
4198
4199                         n = LittleLong(in->textureindex);
4200                         if (n < 0 || n >= loadmodel->brushq3.num_textures)
4201                         {
4202                                 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
4203                                 continue;
4204                         }
4205                         out->texture = loadmodel->brushq3.data_textures + n;
4206                         n = LittleLong(in->effectindex);
4207                         if (n < -1 || n >= loadmodel->brushq3.num_effects)
4208                         {
4209                                 if (developer.integer >= 2)
4210                                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
4211                                 n = -1;
4212                         }
4213                         if (n == -1)
4214                                 out->effect = NULL;
4215                         else
4216                                 out->effect = loadmodel->brushq3.data_effects + n;
4217                         n = LittleLong(in->lightmapindex);
4218                         if (n >= loadmodel->brushq3.num_lightmaps)
4219                         {
4220                                 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
4221                                 n = -1;
4222                         }
4223                         else if (n < 0)
4224                                 n = -1;
4225                         if (n == -1)
4226                                 out->lightmaptexture = NULL;
4227                         else
4228                                 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
4229
4230                         firstvertex = LittleLong(in->firstvertex);
4231                         numvertices = LittleLong(in->numvertices);
4232                         firstelement = LittleLong(in->firstelement);
4233                         numtriangles = LittleLong(in->numelements) / 3;
4234                         if (numtriangles * 3 != LittleLong(in->numelements))
4235                         {
4236                                 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));
4237                                 continue;
4238                         }
4239                         if (firstvertex < 0 || firstvertex + numvertices > loadmodel->brushq3.num_vertices)
4240                         {
4241                                 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);
4242                                 continue;
4243                         }
4244                         if (firstelement < 0 || firstelement + numtriangles * 3 > loadmodel->brushq3.num_triangles * 3)
4245                         {
4246                                 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);
4247                                 continue;
4248                         }
4249                         switch(type)
4250                         {
4251                         case Q3FACETYPE_POLYGON:
4252                         case Q3FACETYPE_MESH:
4253                                 // no processing necessary
4254                                 break;
4255                         case Q3FACETYPE_PATCH:
4256                                 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
4257                                 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
4258                                 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))
4259                                 {
4260                                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
4261                                         continue;
4262                                 }
4263                                 originalvertex3f = loadmodel->brushq3.data_vertex3f + firstvertex * 3;
4264                                 // convert patch to Q3FACETYPE_MESH
4265                                 xtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
4266                                 ytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
4267                                 // bound to user settings
4268                                 xtess = bound(r_subdivisions_mintess.integer, xtess, r_subdivisions_maxtess.integer);
4269                                 ytess = bound(r_subdivisions_mintess.integer, ytess, r_subdivisions_maxtess.integer);
4270                                 // bound to sanity settings
4271                                 xtess = bound(1, xtess, 1024);
4272                                 ytess = bound(1, ytess, 1024);
4273                                 // bound to user limit on vertices
4274                                 while ((xtess > 1 || ytess > 1) && (((patchsize[0] - 1) * xtess) + 1) * (((patchsize[1] - 1) * ytess) + 1) > min(r_subdivisions_maxvertices.integer, 262144))
4275                                 {
4276                                         if (xtess > ytess)
4277                                                 xtess--;
4278                                         else
4279                                                 ytess--;
4280                                 }
4281                                 finalwidth = ((patchsize[0] - 1) * xtess) + 1;
4282                                 finalheight = ((patchsize[1] - 1) * ytess) + 1;
4283                                 numvertices = finalwidth * finalheight;
4284                                 numtriangles = (finalwidth - 1) * (finalheight - 1) * 2;
4285                                 break;
4286                         case Q3FACETYPE_FLARE:
4287                                 if (developer.integer >= 2)
4288                                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
4289                                 // don't render it
4290                                 continue;
4291                         }
4292                         out->mesh.num_vertices = numvertices;
4293                         out->mesh.num_triangles = numtriangles;
4294                         if (meshvertices + out->mesh.num_vertices > 65536)
4295                                 break;
4296                         meshvertices += out->mesh.num_vertices;
4297                         meshtriangles += out->mesh.num_triangles;
4298                 }
4299
4300                 i = oldi;
4301                 in = oldin;
4302                 out = oldout;
4303                 mesh = tempmeshlist[meshnum] = Mod_AllocSurfMesh(loadmodel->mempool, meshvertices, meshtriangles, 0, 0, false, false, true);
4304                 meshvertices = 0;
4305                 meshtriangles = 0;
4306                 for (;i < count && meshvertices + out->mesh.num_vertices <= mesh->num_vertices;i++, in++, out++)
4307                 {
4308                         if (out->mesh.num_vertices < 3 || out->mesh.num_triangles < 1)
4309                                 continue;
4310
4311                         type = LittleLong(in->type);
4312                         firstvertex = LittleLong(in->firstvertex);
4313                         firstelement = LittleLong(in->firstelement);
4314                         out->mesh.data_vertex3f = mesh->data_vertex3f + meshvertices * 3;
4315                         out->mesh.data_svector3f = mesh->data_svector3f + meshvertices * 3;
4316                         out->mesh.data_tvector3f = mesh->data_tvector3f + meshvertices * 3;
4317                         out->mesh.data_normal3f = mesh->data_normal3f + meshvertices * 3;
4318                         out->mesh.data_texcoordtexture2f = mesh->data_texcoordtexture2f + meshvertices * 2;
4319                         out->mesh.data_texcoordlightmap2f = mesh->data_texcoordlightmap2f + meshvertices * 2;
4320                         out->mesh.data_lightmapcolor4f = mesh->data_lightmapcolor4f + meshvertices * 4;
4321                         out->mesh.data_element3i = mesh->data_element3i + meshtriangles * 3;
4322                         out->mesh.data_neighbor3i = mesh->data_neighbor3i + meshtriangles * 3;
4323                         switch(type)
4324                         {
4325                         case Q3FACETYPE_POLYGON:
4326                         case Q3FACETYPE_MESH:
4327                                 // no processing necessary
4328                                 for (j = 0;j < out->mesh.num_vertices;j++)
4329                                 {
4330                                         out->mesh.data_vertex3f[j * 3 + 0] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 0];
4331                                         out->mesh.data_vertex3f[j * 3 + 1] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 1];
4332                                         out->mesh.data_vertex3f[j * 3 + 2] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 2];
4333                                         out->mesh.data_texcoordtexture2f[j * 2 + 0] = loadmodel->brushq3.data_texcoordtexture2f[(firstvertex + j) * 2 + 0];
4334                                         out->mesh.data_texcoordtexture2f[j * 2 + 1] = loadmodel->brushq3.data_texcoordtexture2f[(firstvertex + j) * 2 + 1];
4335                                         out->mesh.data_texcoordlightmap2f[j * 2 + 0] = loadmodel->brushq3.data_texcoordlightmap2f[(firstvertex + j) * 2 + 0];
4336                                         out->mesh.data_texcoordlightmap2f[j * 2 + 1] = loadmodel->brushq3.data_texcoordlightmap2f[(firstvertex + j) * 2 + 1];
4337                                         out->mesh.data_lightmapcolor4f[j * 4 + 0] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 0];
4338                                         out->mesh.data_lightmapcolor4f[j * 4 + 1] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 1];
4339                                         out->mesh.data_lightmapcolor4f[j * 4 + 2] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 2];
4340                                         out->mesh.data_lightmapcolor4f[j * 4 + 3] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 3];
4341                                 }
4342                                 for (j = 0;j < out->mesh.num_triangles*3;j++)
4343                                         out->mesh.data_element3i[j] = loadmodel->brushq3.data_element3i[firstelement + j];
4344                                 break;
4345                         case Q3FACETYPE_PATCH:
4346                                 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
4347                                 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
4348                                 originalvertex3f = loadmodel->brushq3.data_vertex3f + firstvertex * 3;
4349                                 originaltexcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + firstvertex * 2;
4350                                 originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + firstvertex * 2;
4351                                 originalcolor4f = loadmodel->brushq3.data_color4f + firstvertex * 4;
4352                                 // convert patch to Q3FACETYPE_MESH
4353                                 xtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
4354                                 ytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
4355                                 // bound to user settings
4356                                 xtess = bound(r_subdivisions_mintess.integer, xtess, r_subdivisions_maxtess.integer);
4357                                 ytess = bound(r_subdivisions_mintess.integer, ytess, r_subdivisions_maxtess.integer);
4358                                 // bound to sanity settings
4359                                 xtess = bound(1, xtess, 1024);
4360                                 ytess = bound(1, ytess, 1024);
4361                                 // bound to user limit on vertices
4362                                 while ((xtess > 1 || ytess > 1) && (((patchsize[0] - 1) * xtess) + 1) * (((patchsize[1] - 1) * ytess) + 1) > min(r_subdivisions_maxvertices.integer, 262144))
4363                                 {
4364                                         if (xtess > ytess)
4365                                                 xtess--;
4366                                         else
4367                                                 ytess--;
4368                                 }
4369                                 finalwidth = ((patchsize[0] - 1) * xtess) + 1;
4370                                 finalheight = ((patchsize[1] - 1) * ytess) + 1;
4371                                 finalvertices = finalwidth * finalheight;
4372                                 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
4373                                 type = Q3FACETYPE_MESH;
4374                                 // generate geometry
4375                                 // (note: normals are skipped because they get recalculated)
4376                                 Q3PatchTesselateFloat(3, sizeof(float[3]), out->mesh.data_vertex3f, patchsize[0], patchsize[1], sizeof(float[3]), originalvertex3f, xtess, ytess);
4377                                 Q3PatchTesselateFloat(2, sizeof(float[2]), out->mesh.data_texcoordtexture2f, patchsize[0], patchsize[1], sizeof(float[2]), originaltexcoordtexture2f, xtess, ytess);
4378                                 Q3PatchTesselateFloat(2, sizeof(float[2]), out->mesh.data_texcoordlightmap2f, patchsize[0], patchsize[1], sizeof(float[2]), originaltexcoordlightmap2f, xtess, ytess);
4379                                 Q3PatchTesselateFloat(4, sizeof(float[4]), out->mesh.data_lightmapcolor4f, patchsize[0], patchsize[1], sizeof(float[4]), originalcolor4f, xtess, ytess);
4380                                 Q3PatchTriangleElements(out->mesh.data_element3i, finalwidth, finalheight);
4381                                 out->mesh.num_triangles = Mod_RemoveDegenerateTriangles(out->mesh.num_triangles, out->mesh.data_element3i, out->mesh.data_element3i, out->mesh.data_vertex3f);
4382                                 if (developer.integer >= 2)
4383                                 {
4384                                         if (out->mesh.num_triangles < finaltriangles)
4385                                                 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);
4386                                         else
4387                                                 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);
4388                                 }
4389                                 // q3map does not put in collision brushes for curves... ugh
4390                                 // build the lower quality collision geometry
4391                                 xtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value);
4392                                 ytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value);
4393                                 // bound to user settings
4394                                 xtess = bound(r_subdivisions_collision_mintess.integer, xtess, r_subdivisions_collision_maxtess.integer);
4395                                 ytess = bound(r_subdivisions_collision_mintess.integer, ytess, r_subdivisions_collision_maxtess.integer);
4396                                 // bound to sanity settings
4397                                 xtess = bound(1, xtess, 1024);
4398                                 ytess = bound(1, ytess, 1024);
4399                                 // bound to user limit on vertices
4400                                 while ((xtess > 1 || ytess > 1) && (((patchsize[0] - 1) * xtess) + 1) * (((patchsize[1] - 1) * ytess) + 1) > min(r_subdivisions_collision_maxvertices.integer, 262144))
4401                                 {
4402                                         if (xtess > ytess)
4403                                                 xtess--;
4404                                         else
4405                                                 ytess--;
4406                                 }
4407                                 finalwidth = ((patchsize[0] - 1) * xtess) + 1;
4408                                 finalheight = ((patchsize[1] - 1) * ytess) + 1;
4409                                 finalvertices = finalwidth * finalheight;
4410                                 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
4411
4412                                 out->mesh.data_collisionvertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * finalvertices);
4413                                 out->mesh.data_collisionelement3i = Mem_Alloc(loadmodel->mempool, sizeof(int[3]) * finaltriangles);
4414                                 out->mesh.num_collisionvertices = finalvertices;
4415                                 out->mesh.num_collisiontriangles = finaltriangles;
4416                                 Q3PatchTesselateFloat(3, sizeof(float[3]), out->mesh.data_collisionvertex3f, patchsize[0], patchsize[1], sizeof(float[3]), originalvertex3f, xtess, ytess);
4417                                 Q3PatchTriangleElements(out->mesh.data_collisionelement3i, finalwidth, finalheight);
4418
4419                                 //Mod_SnapVertices(3, out->mesh.num_vertices, out->mesh.data_vertex3f, 0.25);
4420                                 Mod_SnapVertices(3, out->mesh.num_collisionvertices, out->mesh.data_collisionvertex3f, 1);
4421
4422                                 oldnumtriangles = out->mesh.num_triangles;
4423                                 oldnumtriangles2 = out->mesh.num_collisiontriangles;
4424                                 out->mesh.num_collisiontriangles = Mod_RemoveDegenerateTriangles(out->mesh.num_collisiontriangles, out->mesh.data_collisionelement3i, out->mesh.data_collisionelement3i, out->mesh.data_collisionvertex3f);
4425                                 if (developer.integer)
4426                                         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);
4427                                 break;
4428                         default:
4429                                 break;
4430                         }
4431                         meshvertices += out->mesh.num_vertices;
4432                         meshtriangles += out->mesh.num_triangles;
4433                         for (j = 0, invalidelements = 0;j < out->mesh.num_triangles * 3;j++)
4434                                 if (out->mesh.data_element3i[j] < 0 || out->mesh.data_element3i[j] >= out->mesh.num_vertices)
4435                                         invalidelements++;
4436                         if (invalidelements)
4437                         {
4438                                 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);
4439                                 for (j = 0;j < out->mesh.num_triangles * 3;j++)
4440                                 {
4441                                         Con_Printf(" %i", out->mesh.data_element3i[j]);
4442                                         if (out->mesh.data_element3i[j] < 0 || out->mesh.data_element3i[j] >= out->mesh.num_vertices)
4443                                                 out->mesh.data_element3i[j] = 0;
4444                                 }
4445                                 Con_Print("\n");
4446                         }
4447                         // for shadow volumes
4448                         Mod_BuildTriangleNeighbors(out->mesh.data_neighbor3i, out->mesh.data_element3i, out->mesh.num_triangles);
4449                         // for per pixel lighting
4450                         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);
4451                         // calculate a bounding box
4452                         VectorClear(out->mins);
4453                         VectorClear(out->maxs);
4454                         if (out->mesh.num_vertices)
4455                         {
4456                                 VectorCopy(out->mesh.data_vertex3f, out->mins);
4457                                 VectorCopy(out->mesh.data_vertex3f, out->maxs);
4458                                 for (j = 1, v = out->mesh.data_vertex3f + 3;j < out->mesh.num_vertices;j++, v += 3)
4459                                 {
4460                                         out->mins[0] = min(out->mins[0], v[0]);
4461                                         out->maxs[0] = max(out->maxs[0], v[0]);
4462                                         out->mins[1] = min(out->mins[1], v[1]);
4463                                         out->maxs[1] = max(out->maxs[1], v[1]);
4464                                         out->mins[2] = min(out->mins[2], v[2]);
4465                                         out->maxs[2] = max(out->maxs[2], v[2]);
4466                                 }
4467                                 out->mins[0] -= 1.0f;
4468                                 out->mins[1] -= 1.0f;
4469                                 out->mins[2] -= 1.0f;
4470                                 out->maxs[0] += 1.0f;
4471                                 out->maxs[1] += 1.0f;
4472                                 out->maxs[2] += 1.0f;
4473                         }
4474                 }
4475         }
4476
4477         // now store the completed list of meshes
4478         loadmodel->nummeshes = meshnum;
4479         if (loadmodel->nummeshes)
4480         {
4481                 loadmodel->meshlist = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t *) * loadmodel->nummeshes);
4482                 memcpy(loadmodel->meshlist, tempmeshlist, sizeof(surfmesh_t *) * loadmodel->nummeshes);
4483         }
4484
4485         // free the no longer needed vertex data
4486         loadmodel->brushq3.num_vertices = 0;
4487         Mem_Free(loadmodel->brushq3.data_vertex3f);
4488         loadmodel->brushq3.data_vertex3f = NULL;
4489         loadmodel->brushq3.data_texcoordtexture2f = NULL;
4490         loadmodel->brushq3.data_texcoordlightmap2f = NULL;
4491         loadmodel->brushq3.data_color4f = NULL;
4492         // free the no longer needed triangle data
4493         loadmodel->brushq3.num_triangles = 0;
4494         Mem_Free(loadmodel->brushq3.data_element3i);
4495         loadmodel->brushq3.data_element3i = NULL;
4496 }
4497
4498 static void Mod_Q3BSP_LoadModels(lump_t *l)
4499 {
4500         q3dmodel_t *in;
4501         q3mmodel_t *out;
4502         int i, j, n, c, count;
4503
4504         in = (void *)(mod_base + l->fileofs);
4505         if (l->filelen % sizeof(*in))
4506                 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
4507         count = l->filelen / sizeof(*in);
4508         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4509
4510         loadmodel->brushq3.data_models = out;
4511         loadmodel->brushq3.num_models = count;
4512
4513         for (i = 0;i < count;i++, in++, out++)
4514         {
4515                 for (j = 0;j < 3;j++)
4516                 {
4517                         out->mins[j] = LittleFloat(in->mins[j]);
4518                         out->maxs[j] = LittleFloat(in->maxs[j]);
4519                 }
4520                 n = LittleLong(in->firstface);
4521                 c = LittleLong(in->numfaces);
4522                 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
4523                         Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
4524                 out->firstface = loadmodel->brushq3.data_faces + n;
4525                 out->numfaces = c;
4526                 n = LittleLong(in->firstbrush);
4527                 c = LittleLong(in->numbrushes);
4528                 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
4529                         Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
4530                 out->firstbrush = loadmodel->brushq3.data_brushes + n;
4531                 out->numbrushes = c;
4532         }
4533 }
4534
4535 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
4536 {
4537         int *in;
4538         int *out;
4539         int i, n, count;
4540
4541         in = (void *)(mod_base + l->fileofs);
4542         if (l->filelen % sizeof(*in))
4543                 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
4544         count = l->filelen / sizeof(*in);
4545         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4546
4547         loadmodel->brush.data_leafbrushes = out;
4548         loadmodel->brush.num_leafbrushes = count;
4549
4550         for (i = 0;i < count;i++, in++, out++)
4551         {
4552                 n = LittleLong(*in);
4553                 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
4554                         Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
4555                 *out = n;
4556         }
4557 }
4558
4559 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
4560 {
4561         int *in;
4562         int *out;
4563         int i, n, count;
4564
4565         in = (void *)(mod_base + l->fileofs);
4566         if (l->filelen % sizeof(*in))
4567                 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
4568         count = l->filelen / sizeof(*in);
4569         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4570
4571         loadmodel->brush.data_leaffaces = out;
4572         loadmodel->brush.num_leaffaces = count;
4573
4574         for (i = 0;i < count;i++, in++, out++)
4575         {
4576                 n = LittleLong(*in);
4577                 if (n < 0 || n >= loadmodel->brushq3.num_faces)
4578                         Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
4579                 *out = n;
4580         }
4581 }
4582
4583 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
4584 {
4585         q3dleaf_t *in;
4586         mleaf_t *out;
4587         int i, j, n, c, count;
4588
4589         in = (void *)(mod_base + l->fileofs);
4590         if (l->filelen % sizeof(*in))
4591                 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
4592         count = l->filelen / sizeof(*in);
4593         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4594
4595         loadmodel->brush.data_leafs = out;
4596         loadmodel->brush.num_leafs = count;
4597
4598         for (i = 0;i < count;i++, in++, out++)
4599         {
4600                 out->parent = NULL;
4601                 out->plane = NULL;
4602                 out->clusterindex = LittleLong(in->clusterindex);
4603                 out->areaindex = LittleLong(in->areaindex);
4604                 for (j = 0;j < 3;j++)
4605                 {
4606                         // yes the mins/maxs are ints
4607                         out->mins[j] = LittleLong(in->mins[j]) - 1;
4608                         out->maxs[j] = LittleLong(in->maxs[j]) + 1;
4609                 }
4610                 n = LittleLong(in->firstleafface);
4611                 c = LittleLong(in->numleaffaces);
4612                 if (n < 0 || n + c > loadmodel->brush.num_leaffaces)
4613                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brush.num_leaffaces);
4614                 out->firstleafface = loadmodel->brush.data_leaffaces + n;
4615                 out->numleaffaces = c;
4616                 n = LittleLong(in->firstleafbrush);
4617                 c = LittleLong(in->numleafbrushes);
4618                 if (n < 0 || n + c > loadmodel->brush.num_leafbrushes)
4619                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brush.num_leafbrushes);
4620                 out->firstleafbrush = loadmodel->brush.data_leafbrushes + n;
4621                 out->numleafbrushes = c;
4622         }
4623 }
4624
4625 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(mnode_t *node, mnode_t *parent)
4626 {
4627         if (node->parent)
4628                 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
4629         node->parent = parent;
4630         if (node->plane)
4631         {
4632                 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
4633                 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
4634         }
4635 }
4636
4637 static void Mod_Q3BSP_LoadNodes(lump_t *l)
4638 {
4639         q3dnode_t *in;
4640         mnode_t *out;
4641         int i, j, n, count;
4642
4643         in = (void *)(mod_base + l->fileofs);
4644         if (l->filelen % sizeof(*in))
4645                 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
4646         count = l->filelen / sizeof(*in);
4647         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4648
4649         loadmodel->brush.data_nodes = out;
4650         loadmodel->brush.num_nodes = count;
4651
4652         for (i = 0;i < count;i++, in++, out++)
4653         {
4654                 out->parent = NULL;
4655                 n = LittleLong(in->planeindex);
4656                 if (n < 0 || n >= loadmodel->brush.num_planes)
4657                         Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brush.num_planes);
4658                 out->plane = loadmodel->brush.data_planes + n;
4659                 for (j = 0;j < 2;j++)
4660                 {
4661                         n = LittleLong(in->childrenindex[j]);
4662                         if (n >= 0)
4663                         {
4664                                 if (n >= loadmodel->brush.num_nodes)
4665                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brush.num_nodes);
4666                                 out->children[j] = loadmodel->brush.data_nodes + n;
4667                         }
4668                         else
4669                         {
4670                                 n = -1 - n;
4671                                 if (n >= loadmodel->brush.num_leafs)
4672                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brush.num_leafs);
4673                                 out->children[j] = (mnode_t *)(loadmodel->brush.data_leafs + n);
4674                         }
4675                 }
4676                 for (j = 0;j < 3;j++)
4677                 {
4678                         // yes the mins/maxs are ints
4679                         out->mins[j] = LittleLong(in->mins[j]) - 1;
4680                         out->maxs[j] = LittleLong(in->maxs[j]) + 1;
4681                 }
4682         }
4683
4684         // set the parent pointers
4685         Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brush.data_nodes, NULL);
4686 }
4687
4688 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
4689 {
4690         q3dlightgrid_t *in;
4691         q3dlightgrid_t *out;
4692         int count;
4693
4694         in = (void *)(mod_base + l->fileofs);
4695         if (l->filelen % sizeof(*in))
4696                 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
4697         loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
4698         loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
4699         loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
4700         loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4701         loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4702         loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4703         loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4704         loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4705         loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4706         loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4707         loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4708         loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4709         count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4710         if (l->filelen)
4711         {
4712                 if (l->filelen < count * (int)sizeof(*in))
4713                         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]);
4714                 if (l->filelen != count * (int)sizeof(*in))
4715                         Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4716         }
4717
4718         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4719         loadmodel->brushq3.data_lightgrid = out;
4720         loadmodel->brushq3.num_lightgrid = count;
4721
4722         // no swapping or validation necessary
4723         if (l->filelen)
4724                 memcpy(out, in, count * (int)sizeof(*out));
4725         else
4726         {
4727                 // no data, fill with white
4728                 int i;
4729                 for (i = 0;i < count;i++)
4730                 {
4731                         out[i].ambientrgb[0] = 128;
4732                         out[i].ambientrgb[1] = 128;
4733                         out[i].ambientrgb[2] = 128;
4734                         out[i].diffusergb[0] = 0;
4735                         out[i].diffusergb[1] = 0;
4736                         out[i].diffusergb[2] = 0;
4737                         out[i].diffusepitch = 0;
4738                         out[i].diffuseyaw = 0;
4739                 }
4740         }
4741
4742         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]);
4743         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]);
4744 }
4745
4746 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4747 {
4748         q3dpvs_t *in;
4749         int totalchains;
4750
4751         if (l->filelen == 0)
4752         {
4753                 int i;
4754                 // unvised maps often have cluster indices even without pvs, so check
4755                 // leafs to find real number of clusters
4756                 loadmodel->brush.num_pvsclusters = 1;
4757                 for (i = 0;i < loadmodel->brush.num_leafs;i++)
4758                         loadmodel->brush.num_pvsclusters = max(loadmodel->brush.num_pvsclusters, loadmodel->brush.data_leafs[i].clusterindex + 1);
4759
4760                 // create clusters
4761                 loadmodel->brush.num_pvsclusterbytes = (loadmodel->brush.num_pvsclusters + 7) / 8;
4762                 totalchains = loadmodel->brush.num_pvsclusterbytes * loadmodel->brush.num_pvsclusters;
4763                 loadmodel->brush.data_pvsclusters = Mem_Alloc(loadmodel->mempool, totalchains);
4764                 memset(loadmodel->brush.data_pvsclusters, 0xFF, totalchains);
4765                 return;
4766         }
4767
4768         in = (void *)(mod_base + l->fileofs);
4769         if (l->filelen < 9)
4770                 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4771
4772         loadmodel->brush.num_pvsclusters = LittleLong(in->numclusters);
4773         loadmodel->brush.num_pvsclusterbytes = LittleLong(in->chainlength);
4774         if (loadmodel->brush.num_pvsclusterbytes < ((loadmodel->brush.num_pvsclusters + 7) / 8))
4775                 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brush.num_pvsclusterbytes, loadmodel->brush.num_pvsclusters);
4776         totalchains = loadmodel->brush.num_pvsclusterbytes * loadmodel->brush.num_pvsclusters;
4777         if (l->filelen < totalchains + (int)sizeof(*in))
4778                 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);
4779
4780         loadmodel->brush.data_pvsclusters = Mem_Alloc(loadmodel->mempool, totalchains);
4781         memcpy(loadmodel->brush.data_pvsclusters, (qbyte *)(in + 1), totalchains);
4782 }
4783
4784 static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4785 {
4786         // FIXME: finish this code
4787         VectorCopy(in, out);
4788 }
4789
4790 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4791 {
4792         int i, j, k, index[3];
4793         float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4794         q3dlightgrid_t *a, *s;
4795         // FIXME: write this
4796         if (!model->brushq3.num_lightgrid)
4797         {
4798                 ambientcolor[0] = 1;
4799                 ambientcolor[1] = 1;
4800                 ambientcolor[2] = 1;
4801                 return;
4802         }
4803         Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4804         //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4805         //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4806         transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0] - 1);
4807         transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1] - 1);
4808         transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2] - 1);
4809         index[0] = (int)floor(transformed[0]);
4810         index[1] = (int)floor(transformed[1]);
4811         index[2] = (int)floor(transformed[2]);
4812         //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4813         // now lerp the values
4814         VectorClear(diffusenormal);
4815         a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4816         for (k = 0;k < 2;k++)
4817         {
4818                 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4819                 if (blend1 < 0.001f || index[2] + k >= model->brushq3.num_lightgrid_isize[2])
4820                         continue;
4821                 for (j = 0;j < 2;j++)
4822                 {
4823                         blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4824                         if (blend2 < 0.001f || index[1] + j >= model->brushq3.num_lightgrid_isize[1])
4825                                 continue;
4826                         for (i = 0;i < 2;i++)
4827                         {
4828                                 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4829                                 if (blend < 0.001f || index[0] + i >= model->brushq3.num_lightgrid_isize[0])
4830                                         continue;
4831                                 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4832                                 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4833                                 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4834                                 pitch = s->diffusepitch * M_PI / 128;
4835                                 yaw = s->diffuseyaw * M_PI / 128;
4836                                 sinpitch = sin(pitch);
4837                                 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4838                                 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4839                                 diffusenormal[2] += blend * (cos(pitch));
4840                                 //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)));
4841                         }
4842                 }
4843         }
4844         VectorNormalize(diffusenormal);
4845         //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]);
4846 }
4847
4848 static void Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace_t *trace, model_t *model, mnode_t *node, const vec3_t point, int markframe)
4849 {
4850         int i;
4851         mleaf_t *leaf;
4852         colbrushf_t *brush;
4853         // find which leaf the point is in
4854         while (node->plane)
4855                 node = node->children[DotProduct(point, node->plane->normal) < node->plane->dist];
4856         // point trace the brushes
4857         leaf = (mleaf_t *)node;
4858         for (i = 0;i < leaf->numleafbrushes;i++)
4859         {
4860                 brush = model->brushq3.data_brushes[leaf->firstleafbrush[i]].colbrushf;
4861                 if (brush && brush->markframe != markframe && BoxesOverlap(point, point, brush->mins, brush->maxs))
4862                 {
4863                         brush->markframe = markframe;
4864                         Collision_TracePointBrushFloat(trace, point, brush);
4865                 }
4866         }
4867         // can't do point traces on curves (they have no thickness)
4868 }
4869
4870 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)
4871 {
4872         int i, startside, endside;
4873         float dist1, dist2, midfrac, mid[3], nodesegmentmins[3], nodesegmentmaxs[3];
4874         mleaf_t *leaf;
4875         q3msurface_t *face;
4876         colbrushf_t *brush;
4877         if (startfrac > trace->realfraction)
4878                 return;
4879         // note: all line fragments past first impact fraction are ignored
4880         if (VectorCompare(start, end))
4881         {
4882                 // find which leaf the point is in
4883                 while (node->plane)
4884                         node = node->children[DotProduct(start, node->plane->normal) < node->plane->dist];
4885         }
4886         else
4887         {
4888                 // find which nodes the line is in and recurse for them
4889                 while (node->plane)
4890                 {
4891                         // recurse down node sides
4892                         dist1 = PlaneDiff(start, node->plane);
4893                         dist2 = PlaneDiff(end, node->plane);
4894                         startside = dist1 < 0;
4895                         endside = dist2 < 0;
4896                         if (startside == endside)
4897                         {
4898                                 // most of the time the line fragment is on one side of the plane
4899                                 node = node->children[startside];
4900                         }
4901                         else
4902                         {
4903                                 // line crosses node plane, split the line
4904                                 midfrac = dist1 / (dist1 - dist2);
4905                                 VectorLerp(start, midfrac, end, mid);
4906                                 // take the near side first
4907                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
4908                                 if (midfrac <= trace->realfraction)
4909                                         Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
4910                                 return;
4911                         }
4912                 }
4913         }
4914         // hit a leaf
4915         nodesegmentmins[0] = min(start[0], end[0]);
4916         nodesegmentmins[1] = min(start[1], end[1]);
4917         nodesegmentmins[2] = min(start[2], end[2]);
4918         nodesegmentmaxs[0] = max(start[0], end[0]);
4919         nodesegmentmaxs[1] = max(start[1], end[1]);
4920         nodesegmentmaxs[2] = max(start[2], end[2]);
4921         // line trace the brushes
4922         leaf = (mleaf_t *)node;
4923         for (i = 0;i < leaf->numleafbrushes;i++)
4924         {
4925                 brush = model->brushq3.data_brushes[leaf->firstleafbrush[i]].colbrushf;
4926                 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
4927                 {
4928                         brush->markframe = markframe;
4929                         Collision_TraceLineBrushFloat(trace, linestart, lineend, brush, brush);
4930                         if (startfrac > trace->realfraction)
4931                                 return;
4932                 }
4933         }
4934         // can't do point traces on curves (they have no thickness)
4935         if (mod_q3bsp_curves_collisions.integer && !VectorCompare(start, end))
4936         {
4937                 // line trace the curves
4938                 for (i = 0;i < leaf->numleaffaces;i++)
4939                 {
4940                         face = model->brushq3.data_faces + leaf->firstleafface[i];
4941                         if (face->mesh.num_collisiontriangles && face->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
4942                         {
4943                                 face->collisionmarkframe = markframe;
4944                                 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->mesh.num_collisiontriangles, face->mesh.data_collisionelement3i, face->mesh.data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4945                                 if (startfrac > trace->realfraction)
4946                                         return;
4947                         }
4948                 }
4949         }
4950 }
4951
4952 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)
4953 {
4954         int i;
4955         //int sides;
4956         float nodesegmentmins[3], nodesegmentmaxs[3];
4957         mleaf_t *leaf;
4958         colbrushf_t *brush;
4959         q3msurface_t *face;
4960         /*
4961                 // find which nodes the line is in and recurse for them
4962                 while (node->plane)
4963                 {
4964                         // recurse down node sides
4965                         int startside, endside;
4966                         float dist1near, dist1far, dist2near, dist2far;
4967                         BoxPlaneCornerDistances(thisbrush_start->mins, thisbrush_start->maxs, node->plane, &dist1near, &dist1far);
4968                         BoxPlaneCornerDistances(thisbrush_end->mins, thisbrush_end->maxs, node->plane, &dist2near, &dist2far);
4969                         startside = dist1near < 0;
4970                         startside = dist1near < 0 ? (dist1far < 0 ? 1 : 2) : (dist1far < 0 ? 2 : 0);
4971                         endside = dist2near < 0 ? (dist2far < 0 ? 1 : 2) : (dist2far < 0 ? 2 : 0);
4972                         if (startside == 2 || endside == 2)
4973                         {
4974                                 // brushes cross plane
4975                                 // do not clip anything, just take both sides
4976                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4977                                 node = node->children[1];
4978                                 continue;
4979                         }
4980                         if (startside == 0)
4981                         {
4982                                 if (endside == 0)
4983                                 {
4984                                         node = node->children[0];
4985                                         continue;
4986                                 }
4987                                 else
4988                                 {
4989                                         //midf0 = dist1near / (dist1near - dist2near);
4990                                         //midf1 = dist1far / (dist1far - dist2far);
4991                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4992                                         node = node->children[1];
4993                                         continue;
4994                                 }
4995                         }
4996                         else
4997                         {
4998                                 if (endside == 0)
4999                                 {
5000                                         //midf0 = dist1near / (dist1near - dist2near);
5001                                         //midf1 = dist1far / (dist1far - dist2far);
5002                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5003                                         node = node->children[1];
5004                                         continue;
5005                                 }
5006                                 else
5007                                 {
5008                                         node = node->children[1];
5009                                         continue;
5010                                 }
5011                         }
5012
5013                         if (dist1near <  0 && dist2near <  0 && dist1far <  0 && dist2far <  0){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){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
5016                         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;}
5017                         if (dist1near <  0 && dist2near >= 0 && dist1far <  0 && dist2far <  0){node = node->children[1];continue;}
5018                         if (dist1near <  0 && dist2near >= 0 && dist1far <  0 && dist2far >= 0){}
5019                         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;}
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){node = node->children[1];continue;}
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){}
5024                         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;}
5025                         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;}
5026                         if (dist1near >= 0 && dist2near >= 0 && dist1far <  0 && dist2far >= 0){node = node->children[0];continue;}
5027                         if (dist1near >= 0 && dist2near >= 0 && dist1far >= 0 && dist2far <  0){node = node->children[0];continue;}
5028                         if (dist1near >= 0 && dist2near >= 0 && dist1far >= 0 && dist2far >= 0){node = node->children[0];continue;}
5029                         {
5030                                 if (dist2near < 0) // d1n<0 && d2n<0
5031                                 {
5032                                         if (dist2near < 0) // d1n<0 && d2n<0
5033                                         {
5034                                                 if (dist2near < 0) // d1n<0 && d2n<0
5035                                                 {
5036                                                 }
5037                                                 else // d1n<0 && d2n>0
5038                                                 {
5039                                                 }
5040                                         }
5041                                         else // d1n<0 && d2n>0
5042                                         {
5043                                                 if (dist2near < 0) // d1n<0 && d2n<0
5044                                                 {
5045                                                 }
5046                                                 else // d1n<0 && d2n>0
5047                                                 {
5048                                                 }
5049                                         }
5050                                 }
5051                                 else // d1n<0 && d2n>0
5052                                 {
5053                                 }
5054                         }
5055                         else // d1n>0
5056                         {
5057                                 if (dist2near < 0) // d1n>0 && d2n<0
5058                                 {
5059                                 }
5060                                 else // d1n>0 && d2n>0
5061                                 {
5062                                 }
5063                         }
5064                         if (dist1near < 0 == dist1far < 0 == dist2near < 0 == dist2far < 0)
5065                         {
5066                                 node = node->children[startside];
5067                                 continue;
5068                         }
5069                         if (dist1near < dist2near)
5070                         {
5071                                 // out
5072                                 if (dist1near >= 0)
5073                                 {
5074                                         node = node->children[0];
5075                                         continue;
5076                                 }
5077                                 if (dist2far < 0)
5078                                 {
5079                                         node = node->children[1];
5080                                         continue;
5081                                 }
5082                                 // dist1near < 0 && dist2far >= 0
5083                         }
5084                         else
5085                         {
5086                                 // in
5087                         }
5088                         startside = dist1near < 0 ? (dist1far < 0 ? 1 : 2) : (dist1far < 0 ? 2 : 0);
5089                         endside = dist2near < 0 ? (dist2far < 0 ? 1 : 2) : (dist2far < 0 ? 2 : 0);
5090                         if (startside == 2 || endside == 2)
5091                         {
5092                                 // brushes cross plane
5093                                 // do not clip anything, just take both sides
5094                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5095                                 node = node->children[1];
5096                         }
5097                         else if (startside == endside)
5098                                 node = node->children[startside];
5099                         else if (startside == 0) // endside = 1 (start infront, end behind)
5100                         {
5101                         }
5102                         else // startside == 1 endside = 0 (start behind, end infront)
5103                         {
5104                         }
5105                         == endside)
5106                         {
5107                                 if (startside < 2)
5108                                         node = node->children[startside];
5109                                 else
5110                                 {
5111                                         // start and end brush cross plane
5112                                 }
5113                         }
5114                         else
5115                         {
5116                         }
5117                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5118                                 node = node->children[1];
5119                         else if (dist1near < 0 && dist1far < 0 && dist2near >= 0 && dist2far >= 0)
5120                         else if (dist1near >= 0 && dist1far >= 0 && dist2near < 0 && dist2far < 0)
5121                         else if (dist1near >= 0 && dist1far >= 0 && dist2near >= 0 && dist2far >= 0)
5122                                 node = node->children[0];
5123                         else
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                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5128                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5129                         {
5130                         }
5131                         else if (dist1near >= 0 && dist1far >= 0)
5132                         {
5133                         }
5134                         else // mixed (lying on plane)
5135                         {
5136                         }
5137                         {
5138                                 if (dist2near < 0 && dist2far < 0)
5139                                 {
5140                                 }
5141                                 else
5142                                         node = node->children[1];
5143                         }
5144                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5145                                 node = node->children[0];
5146                         else if (dist1near >= 0 && dist1far >= 0 && dist2near >= 0 && dist2far >= 0)
5147                                 node = node->children[1];
5148                         else
5149                         {
5150                                 // both sides
5151                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
5152                                 node = node->children[1];
5153                         }
5154                         sides = dist1near || dist1near < 0 | dist1far < 0 | dist2near < 0 | dist
5155                         startside = dist1 < 0;
5156                         endside = dist2 < 0;
5157                         if (startside == endside)
5158                         {
5159                                 // most of the time the line fragment is on one side of the plane
5160                                 node = node->children[startside];
5161                         }
5162                         else
5163                         {
5164                                 // line crosses node plane, split the line
5165                                 midfrac = dist1 / (dist1 - dist2);
5166                                 VectorLerp(start, midfrac, end, mid);
5167                                 // take the near side first
5168                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
5169                                 if (midfrac <= trace->fraction)
5170                                         Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
5171                                 return;
5172                         }
5173                 }
5174         */
5175 #if 1
5176         for (;;)
5177         {
5178                 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5179                 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5180                 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5181                 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5182                 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5183                 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5184                 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
5185                         return;
5186                 if (!node->plane)
5187                         break;
5188                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5189                 node = node->children[1];
5190         }
5191 #elif 0
5192         // FIXME: could be made faster by copying TraceLine code and making it use
5193         // box plane distances...  (variant on the BoxOnPlaneSide code)
5194         for (;;)
5195         {
5196                 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5197                 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5198                 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5199                 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5200                 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5201                 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5202                 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
5203                         return;
5204                 if (!node->plane)
5205                         break;
5206                 if (mod_q3bsp_debugtracebrush.integer == 2)
5207                 {
5208                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5209                         node = node->children[1];
5210                         continue;
5211                 }
5212                 else if (mod_q3bsp_debugtracebrush.integer == 1)
5213                 {
5214                         // recurse down node sides
5215                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5216                         if (sides == 3)
5217                         {
5218                                 // segment box crosses plane
5219                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5220                                 node = node->children[1];
5221                                 continue;
5222                         }
5223                         // take whichever side the segment box is on
5224                         node = node->children[sides - 1];
5225                         continue;
5226                 }
5227                 else
5228                 {
5229                         // recurse down node sides
5230                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5231                         if (sides == 3)
5232                         {
5233                                 // segment box crosses plane
5234                                 // now check start and end brush boxes to handle a lot of 'diagonal' cases more efficiently...
5235                                 sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, node->plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, node->plane);
5236                                 if (sides == 3)
5237                                 {
5238                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5239                                         node = node->children[1];
5240                                         continue;
5241                                 }
5242                         }
5243                         // take whichever side the segment box is on
5244                         node = node->children[sides - 1];
5245                         continue;
5246                 }
5247                 return;
5248         }
5249 #else
5250         // FIXME: could be made faster by copying TraceLine code and making it use
5251         // box plane distances...  (variant on the BoxOnPlaneSide code)
5252         for (;;)
5253         {
5254                 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5255                 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5256                 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5257                 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5258                 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5259                 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5260                 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
5261                         return;
5262                 if (!node->plane)
5263                         break;
5264                 if (mod_q3bsp_debugtracebrush.integer == 2)
5265                 {
5266                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5267                         node = node->children[1];
5268                 }
5269                 else if (mod_q3bsp_debugtracebrush.integer == 1)
5270                 {
5271                         // recurse down node sides
5272                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5273                         if (sides == 3)
5274                         {
5275                                 // segment box crosses plane
5276                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5277                                 node = node->children[1];
5278                         }
5279                         else
5280                         {
5281                                 // take whichever side the segment box is on
5282                                 node = node->children[sides - 1];
5283                         }
5284                 }
5285                 else
5286                 {
5287                         // recurse down node sides
5288                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5289                         if (sides == 3)
5290                         {
5291                                 // segment box crosses plane
5292                                 // now check start and end brush boxes to handle a lot of 'diagonal' cases more efficiently...
5293                                 sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, node->plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, node->plane);
5294                                 if (sides == 3)
5295                                 {
5296                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5297                                         sides = 2;
5298                                 }
5299                         }
5300                         // take whichever side the segment box is on
5301                         node = node->children[sides - 1];
5302                 }
5303         }
5304 #endif
5305         // hit a leaf
5306         leaf = (mleaf_t *)node;
5307         for (i = 0;i < leaf->numleafbrushes;i++)
5308         {
5309                 brush = model->brushq3.data_brushes[leaf->firstleafbrush[i]].colbrushf;
5310                 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
5311                 {
5312                         brush->markframe = markframe;
5313                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, brush, brush);
5314                 }
5315         }
5316         if (mod_q3bsp_curves_collisions.integer)
5317         {
5318                 for (i = 0;i < leaf->numleaffaces;i++)
5319                 {
5320                         face = model->brushq3.data_faces + leaf->firstleafface[i];
5321                         if (face->mesh.num_collisiontriangles && face->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
5322                         {
5323                                 face->collisionmarkframe = markframe;
5324                                 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->mesh.num_collisiontriangles, face->mesh.data_collisionelement3i, face->mesh.data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
5325                         }
5326                 }
5327         }
5328 }
5329
5330 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)
5331 {
5332         int i;
5333         float segmentmins[3], segmentmaxs[3];
5334         colbrushf_t *thisbrush_start, *thisbrush_end;
5335         matrix4x4_t startmatrix, endmatrix;
5336         static int markframe = 0;
5337         q3msurface_t *face;
5338         memset(trace, 0, sizeof(*trace));
5339         trace->fraction = 1;
5340         trace->realfraction = 1;
5341         trace->hitsupercontentsmask = hitsupercontentsmask;
5342         Matrix4x4_CreateIdentity(&startmatrix);
5343         Matrix4x4_CreateIdentity(&endmatrix);
5344         segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
5345         segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
5346         segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
5347         segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
5348         segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
5349         segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
5350         if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
5351         {
5352                 if (VectorCompare(boxstartmins, boxendmins))
5353                 {
5354                         // point trace
5355                         if (model->brush.submodel)
5356                         {
5357                                 for (i = 0;i < model->brushq3.data_models[model->brush.submodel].numbrushes;i++)
5358                                         if (model->brushq3.data_models[model->brush.submodel].firstbrush[i].colbrushf)
5359                                                 Collision_TracePointBrushFloat(trace, boxstartmins, model->brushq3.data_models[model->brush.submodel].firstbrush[i].colbrushf);
5360                         }
5361                         else
5362                                 Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace, model, model->brush.data_nodes, boxstartmins, ++markframe);
5363                 }
5364                 else
5365                 {
5366                         // line trace
5367                         if (model->brush.submodel)
5368                         {
5369                                 for (i = 0;i < model->brushq3.data_models[model->brush.submodel].numbrushes;i++)
5370                                         if (model->brushq3.data_models[model->brush.submodel].firstbrush[i].colbrushf)
5371                                                 Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_models[model->brush.submodel].firstbrush[i].colbrushf, model->brushq3.data_models[model->brush.submodel].firstbrush[i].colbrushf);
5372                                 if (mod_q3bsp_curves_collisions.integer)
5373                                 {
5374                                         for (i = 0;i < model->brushq3.data_models[model->brush.submodel].numfaces;i++)
5375                                         {
5376                                                 face = model->brushq3.data_models[model->brush.submodel].firstface + i;
5377                                                 if (face->mesh.num_collisiontriangles)
5378                                                         Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->mesh.num_collisiontriangles, face->mesh.data_collisionelement3i, face->mesh.data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
5379                                         }
5380                                 }
5381                         }
5382                         else
5383                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, model->brush.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe, segmentmins, segmentmaxs);
5384                 }
5385         }
5386         else
5387         {
5388                 // box trace, performed as brush trace
5389                 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
5390                 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
5391                 if (model->brush.submodel)
5392                 {
5393                         for (i = 0;i < model->brushq3.data_models[model->brush.submodel].numbrushes;i++)
5394                                 if (model->brushq3.data_models[model->brush.submodel].firstbrush[i].colbrushf)
5395                                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_models[model->brush.submodel].firstbrush[i].colbrushf, model->brushq3.data_models[model->brush.submodel].firstbrush[i].colbrushf);
5396                         if (mod_q3bsp_curves_collisions.integer)
5397                         {
5398                                 for (i = 0;i < model->brushq3.data_models[model->brush.submodel].numfaces;i++)
5399                                 {
5400                                         face = model->brushq3.data_models[model->brush.submodel].firstface + i;
5401                                         if (face->mesh.num_collisiontriangles)
5402                                                 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->mesh.num_collisiontriangles, face->mesh.data_collisionelement3i, face->mesh.data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
5403                                 }
5404                         }
5405                 }
5406                 else
5407                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, model->brush.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
5408         }
5409 }
5410
5411 static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
5412 {
5413         int clusterindex, side, nodestackindex = 0;
5414         mnode_t *node, *nodestack[1024];
5415         node = model->brush.data_nodes;
5416         if (!model->brush.num_pvsclusters)
5417                 return true;
5418         for (;;)
5419         {
5420                 if (node->plane)
5421                 {
5422                         // node - recurse down the BSP tree
5423                         side = BoxOnPlaneSide(mins, maxs, node->plane) - 1;
5424                         if (side < 2)
5425                         {
5426                                 // box is on one side of plane, take that path
5427                                 node = node->children[side];
5428                         }
5429                         else
5430                         {
5431                                 // box crosses plane, take one path and remember the other
5432                                 if (nodestackindex < 1024)
5433                                         nodestack[nodestackindex++] = node->children[0];
5434                                 node = node->children[1];
5435                         }
5436                 }
5437                 else
5438                 {
5439                         // leaf - check cluster bit
5440                         clusterindex = ((mleaf_t *)node)->clusterindex;
5441 #if 0
5442                         if (clusterindex >= model->brush.num_pvsclusters)
5443                         {
5444                                 Con_Printf("%i >= %i\n", clusterindex, model->brush.num_pvsclusters);
5445                                 return true;
5446                         }
5447 #endif
5448                         if (CHECKPVSBIT(pvs, clusterindex))
5449                         {
5450                                 // it is visible, return immediately with the news
5451                                 return true;
5452                         }
5453                         else
5454                         {
5455                                 // nothing to see here, try another path we didn't take earlier
5456                                 if (nodestackindex == 0)
5457                                         break;
5458                                 node = nodestack[--nodestackindex];
5459                         }
5460                 }
5461         }
5462         // it is not visible
5463         return false;
5464 }
5465
5466 //Returns PVS data for a given point
5467 //(note: can return NULL)
5468 static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
5469 {
5470         mnode_t *node;
5471         Mod_CheckLoaded(model);
5472         node = model->brush.data_nodes;
5473         while (node->plane)
5474                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
5475         if (((mleaf_t *)node)->clusterindex >= 0)
5476                 return model->brush.data_pvsclusters + ((mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
5477         else
5478                 return NULL;
5479 }
5480
5481 static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node)
5482 {
5483         while (node->plane)
5484         {
5485                 float d = PlaneDiff(org, node->plane);
5486                 if (d > radius)
5487                         node = node->children[0];
5488                 else if (d < -radius)
5489                         node = node->children[1];
5490                 else
5491                 {
5492                         // go down both sides
5493                         Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
5494                         node = node->children[1];
5495                 }
5496         }
5497         // if this leaf is in a cluster, accumulate the pvs bits
5498         if (((mleaf_t *)node)->clusterindex >= 0)
5499         {
5500                 int i;
5501                 qbyte *pvs = model->brush.data_pvsclusters + ((mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
5502                 for (i = 0;i < pvsbytes;i++)
5503                         pvsbuffer[i] |= pvs[i];
5504         }
5505 }
5506
5507 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
5508 //of the given point.
5509 static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
5510 {
5511         int bytes = model->brush.num_pvsclusterbytes;
5512         bytes = min(bytes, pvsbufferlength);
5513         if (r_novis.integer || !model->brush.num_pvsclusters || !Mod_Q3BSP_GetPVS(model, org))
5514         {
5515                 memset(pvsbuffer, 0xFF, bytes);
5516                 return bytes;
5517         }
5518         memset(pvsbuffer, 0, bytes);
5519         Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brush.data_nodes);
5520         return bytes;
5521 }
5522
5523
5524 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
5525 {
5526         int supercontents = 0;
5527         if (nativecontents & CONTENTSQ3_SOLID)
5528                 supercontents |= SUPERCONTENTS_SOLID;
5529         if (nativecontents & CONTENTSQ3_WATER)
5530                 supercontents |= SUPERCONTENTS_WATER;
5531         if (nativecontents & CONTENTSQ3_SLIME)
5532                 supercontents |= SUPERCONTENTS_SLIME;
5533         if (nativecontents & CONTENTSQ3_LAVA)
5534                 supercontents |= SUPERCONTENTS_LAVA;
5535         if (nativecontents & CONTENTSQ3_BODY)
5536                 supercontents |= SUPERCONTENTS_BODY;
5537         if (nativecontents & CONTENTSQ3_CORPSE)
5538                 supercontents |= SUPERCONTENTS_CORPSE;
5539         if (nativecontents & CONTENTSQ3_NODROP)
5540                 supercontents |= SUPERCONTENTS_NODROP;
5541         return supercontents;
5542 }
5543
5544 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
5545 {
5546         int nativecontents = 0;
5547         if (supercontents & SUPERCONTENTS_SOLID)
5548                 nativecontents |= CONTENTSQ3_SOLID;
5549         if (supercontents & SUPERCONTENTS_WATER)
5550                 nativecontents |= CONTENTSQ3_WATER;
5551         if (supercontents & SUPERCONTENTS_SLIME)
5552                 nativecontents |= CONTENTSQ3_SLIME;
5553         if (supercontents & SUPERCONTENTS_LAVA)
5554                 nativecontents |= CONTENTSQ3_LAVA;
5555         if (supercontents & SUPERCONTENTS_BODY)
5556                 nativecontents |= CONTENTSQ3_BODY;
5557         if (supercontents & SUPERCONTENTS_CORPSE)
5558                 nativecontents |= CONTENTSQ3_CORPSE;
5559         if (supercontents & SUPERCONTENTS_NODROP)
5560                 nativecontents |= CONTENTSQ3_NODROP;
5561         return nativecontents;
5562 }
5563
5564 void Mod_Q3BSP_RecursiveFindNumLeafs(mnode_t *node)
5565 {
5566         int numleafs;
5567         while (node->plane)
5568         {
5569                 Mod_Q3BSP_RecursiveFindNumLeafs(node->children[0]);
5570                 node = node->children[1];
5571         }
5572         numleafs = ((mleaf_t *)node - loadmodel->brush.data_leafs) + 1;
5573         if (loadmodel->brush.num_leafs < numleafs)
5574                 loadmodel->brush.num_leafs = numleafs;
5575 }
5576
5577 extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
5578 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
5579 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);
5580 extern void R_Q3BSP_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, int numsurfaces, const int *surfacelist);
5581 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);
5582 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
5583 {
5584         int i, j, numshadowmeshtriangles;
5585         q3dheader_t *header;
5586         float corner[3], yawradius, modelradius;
5587         q3msurface_t *face;
5588
5589         mod->type = mod_brushq3;
5590         mod->numframes = 2; // although alternate textures are not supported it is annoying to complain about no such frame 1
5591         mod->numskins = 1;
5592
5593         header = (q3dheader_t *)buffer;
5594
5595         i = LittleLong(header->version);
5596         if (i != Q3BSPVERSION)
5597                 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
5598         if (mod->isworldmodel)
5599                 Cvar_SetValue("halflifebsp", false);
5600
5601         mod->soundfromcenter = true;
5602         mod->TraceBox = Mod_Q3BSP_TraceBox;
5603         mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
5604         mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
5605         mod->brush.GetPVS = Mod_Q3BSP_GetPVS;
5606         mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
5607         mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
5608         mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
5609         mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
5610         //mod->DrawSky = R_Q3BSP_DrawSky;
5611         mod->Draw = R_Q3BSP_Draw;
5612         mod->GetLightInfo = R_Q3BSP_GetLightInfo;
5613         mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
5614         mod->DrawLight = R_Q3BSP_DrawLight;
5615
5616         mod_base = (qbyte *)header;
5617
5618         // swap all the lumps
5619         header->ident = LittleLong(header->ident);
5620         header->version = LittleLong(header->version);
5621         for (i = 0;i < Q3HEADER_LUMPS;i++)
5622         {
5623                 header->lumps[i].fileofs = LittleLong(header->lumps[i].fileofs);
5624                 header->lumps[i].filelen = LittleLong(header->lumps[i].filelen);
5625         }
5626
5627         Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
5628         Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
5629         Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
5630         Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
5631         Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
5632         Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
5633         Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
5634         Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
5635         Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
5636         Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
5637         Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
5638         Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
5639         Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
5640         Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
5641         Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
5642         Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
5643         Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
5644         loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
5645
5646         // make a single combined shadow mesh to allow optimized shadow volume creation
5647         numshadowmeshtriangles = 0;
5648         for (j = 0, face = loadmodel->brushq3.data_faces;j < loadmodel->brushq3.num_faces;j++, face++)
5649         {
5650                 face->num_firstshadowmeshtriangle = numshadowmeshtriangles;
5651                 numshadowmeshtriangles += face->mesh.num_triangles;
5652         }
5653         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
5654         for (j = 0, face = loadmodel->brushq3.data_faces;j < loadmodel->brushq3.num_faces;j++, face++)
5655                 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);
5656         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true);
5657         Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
5658
5659         loadmodel->brush.num_leafs = 0;
5660         Mod_Q3BSP_RecursiveFindNumLeafs(loadmodel->brush.data_nodes);
5661
5662         mod = loadmodel;
5663         for (i = 0;i < loadmodel->brushq3.num_models;i++)
5664         {
5665                 if (i > 0)
5666                 {
5667                         char name[10];
5668                         // LordHavoc: only register submodels if it is the world
5669                         // (prevents external bsp models from replacing world submodels with
5670                         //  their own)
5671                         if (!loadmodel->isworldmodel)
5672                                 continue;
5673                         // duplicate the basic information
5674                         sprintf(name, "*%i", i);
5675                         mod = Mod_FindName(name);
5676                         *mod = *loadmodel;
5677                         strcpy(mod->name, name);
5678                         // textures and memory belong to the main model
5679                         mod->texturepool = NULL;
5680                         mod->mempool = NULL;
5681                         mod->brush.GetPVS = NULL;
5682                         mod->brush.FatPVS = NULL;
5683                         mod->brush.BoxTouchingPVS = NULL;
5684                         mod->brush.LightPoint = NULL;
5685                         mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
5686                 }
5687                 mod->brush.submodel = i;
5688
5689                 // make the model surface list (used by shadowing/lighting)
5690                 mod->nummodelsurfaces = mod->brushq3.data_models[i].numfaces;
5691                 mod->surfacelist = Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->surfacelist));
5692                 for (j = 0;j < mod->nummodelsurfaces;j++)
5693                         mod->surfacelist[j] = (mod->brushq3.data_models[i].firstface - mod->brushq3.data_faces) + j;
5694
5695                 VectorCopy(mod->brushq3.data_models[i].mins, mod->normalmins);
5696                 VectorCopy(mod->brushq3.data_models[i].maxs, mod->normalmaxs);
5697                 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
5698                 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
5699                 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
5700                 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
5701                 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
5702                 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
5703                 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
5704                 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
5705                 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
5706                 mod->yawmins[2] = mod->normalmins[2];
5707                 mod->yawmaxs[2] = mod->normalmaxs[2];
5708                 mod->radius = modelradius;
5709                 mod->radius2 = modelradius * modelradius;
5710
5711                 for (j = 0;j < mod->brushq3.data_models[i].numfaces;j++)
5712                         if (mod->brushq3.data_models[i].firstface[j].texture->surfaceflags & Q3SURFACEFLAG_SKY)
5713                                 break;
5714                 if (j < mod->brushq3.data_models[i].numfaces)
5715                         mod->DrawSky = R_Q3BSP_DrawSky;
5716         }
5717 }
5718
5719 void Mod_IBSP_Load(model_t *mod, void *buffer)
5720 {
5721         int i = LittleLong(((int *)buffer)[1]);
5722         if (i == Q3BSPVERSION)
5723                 Mod_Q3BSP_Load(mod,buffer);
5724         else if (i == Q2BSPVERSION)
5725                 Mod_Q2BSP_Load(mod,buffer);
5726         else
5727                 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
5728 }
5729
5730 void Mod_MAP_Load(model_t *mod, void *buffer)
5731 {
5732         Host_Error("Mod_MAP_Load: not yet implemented\n");
5733 }
5734