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