]> icculus.org git repositories - divverent/darkplaces.git/blob - model_brush.c
forgot to remove a debugging test that emitted sparks from autosprite2 shaders
[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_minlevel = {0, "r_subdivisions_minlevel", "0"};
39 cvar_t r_subdivisions_maxlevel = {0, "r_subdivisions_maxlevel", "10"};
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_minlevel = {0, "r_subdivisions_collision_minlevel", "0"};
43 cvar_t r_subdivisions_collision_maxlevel = {0, "r_subdivisions_collision_maxlevel", "10"};
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_minlevel);
60         Cvar_RegisterVariable(&r_subdivisions_maxlevel);
61         Cvar_RegisterVariable(&r_subdivisions_maxvertices);
62         Cvar_RegisterVariable(&r_subdivisions_collision_tolerance);
63         Cvar_RegisterVariable(&r_subdivisions_collision_minlevel);
64         Cvar_RegisterVariable(&r_subdivisions_collision_maxlevel);
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                                                                         Con_Printf("%s %i: ", shadername, passnumber);
3667                                                                         for (j = 0;j < numparameters;j++)
3668                                                                                 Con_Printf(" %s", parameter[j]);
3669                                                                         Con_Print("\n");
3670                                                                         if (passnumber == 0 && numparameters >= 1)
3671                                                                         {
3672                                                                                 if (!strcasecmp(parameter[0], "blendfunc"))
3673                                                                                 {
3674                                                                                         Con_Printf("!\n");
3675                                                                                         if (numparameters == 2 && !strcasecmp(parameter[1], "add"))
3676                                                                                                 flags2 |= Q3TEXTUREFLAG_ADDITIVE;
3677                                                                                         else if (numparameters == 3 && !strcasecmp(parameter[1], "gl_one") && !strcasecmp(parameter[2], "gl_one"))
3678                                                                                                 flags2 |= Q3TEXTUREFLAG_ADDITIVE;
3679                                                                                         else if (numparameters == 3 && !strcasecmp(parameter[1], "gl_src_alpha") && !strcasecmp(parameter[2], "gl_one"))
3680                                                                                                 flags2 |= Q3TEXTUREFLAG_ADDITIVE;
3681                                                                                 }
3682                                                                                 else if (numparameters >= 2 && (!strcasecmp(parameter[0], "map") || !strcasecmp(parameter[0], "clampmap")))
3683                                                                                         strlcpy(firstpasstexturename, parameter[1], sizeof(firstpasstexturename));
3684                                                                                 else if (numparameters >= 3 && !strcasecmp(parameter[0], "animmap"))
3685                                                                                         strlcpy(firstpasstexturename, parameter[2], sizeof(firstpasstexturename));
3686                                                                         }
3687                                                                         // break out a level if it was }
3688                                                                         if (!strcasecmp(com_token, "}"))
3689                                                                                 break;
3690                                                                 }
3691                                                                 passnumber++;
3692                                                                 continue;
3693                                                         }
3694                                                         numparameters = 0;
3695                                                         for (j = 0;strcasecmp(com_token, "\n") && strcasecmp(com_token, "}");j++)
3696                                                         {
3697                                                                 if (j < 4)
3698                                                                 {
3699                                                                         strlcpy(parameter[j], com_token, sizeof(parameter[j]));
3700                                                                         numparameters = j + 1;
3701                                                                 }
3702                                                                 if (!COM_ParseToken(&text, true))
3703                                                                         break;
3704                                                         }
3705                                                         if (i == 0 && !strcasecmp(com_token, "}"))
3706                                                                 break;
3707                                                         Con_Printf("%s: ", shadername);
3708                                                         for (j = 0;j < numparameters;j++)
3709                                                                 Con_Printf(" %s", parameter[j]);
3710                                                         Con_Print("\n");
3711                                                         if (numparameters < 1)
3712                                                                 continue;
3713                                                         if (!strcasecmp(parameter[0], "surfaceparm") && numparameters >= 2)
3714                                                         {
3715                                                                 if (!strcasecmp(parameter[1], "alphashadow"))
3716                                                                         flags |= Q3SURFACEPARM_ALPHASHADOW;
3717                                                                 else if (!strcasecmp(parameter[1], "areaportal"))
3718                                                                         flags |= Q3SURFACEPARM_AREAPORTAL;
3719                                                                 else if (!strcasecmp(parameter[1], "clusterportal"))
3720                                                                         flags |= Q3SURFACEPARM_CLUSTERPORTAL;
3721                                                                 else if (!strcasecmp(parameter[1], "detail"))
3722                                                                         flags |= Q3SURFACEPARM_DETAIL;
3723                                                                 else if (!strcasecmp(parameter[1], "donotenter"))
3724                                                                         flags |= Q3SURFACEPARM_DONOTENTER;
3725                                                                 else if (!strcasecmp(parameter[1], "fog"))
3726                                                                         flags |= Q3SURFACEPARM_FOG;
3727                                                                 else if (!strcasecmp(parameter[1], "lava"))
3728                                                                         flags |= Q3SURFACEPARM_LAVA;
3729                                                                 else if (!strcasecmp(parameter[1], "lightfilter"))
3730                                                                         flags |= Q3SURFACEPARM_LIGHTFILTER;
3731                                                                 else if (!strcasecmp(parameter[1], "metalsteps"))
3732                                                                         flags |= Q3SURFACEPARM_METALSTEPS;
3733                                                                 else if (!strcasecmp(parameter[1], "nodamage"))
3734                                                                         flags |= Q3SURFACEPARM_NODAMAGE;
3735                                                                 else if (!strcasecmp(parameter[1], "nodlight"))
3736                                                                         flags |= Q3SURFACEPARM_NODLIGHT;
3737                                                                 else if (!strcasecmp(parameter[1], "nodraw"))
3738                                                                         flags |= Q3SURFACEPARM_NODRAW;
3739                                                                 else if (!strcasecmp(parameter[1], "nodrop"))
3740                                                                         flags |= Q3SURFACEPARM_NODROP;
3741                                                                 else if (!strcasecmp(parameter[1], "noimpact"))
3742                                                                         flags |= Q3SURFACEPARM_NOIMPACT;
3743                                                                 else if (!strcasecmp(parameter[1], "nolightmap"))
3744                                                                         flags |= Q3SURFACEPARM_NOLIGHTMAP;
3745                                                                 else if (!strcasecmp(parameter[1], "nomarks"))
3746                                                                         flags |= Q3SURFACEPARM_NOMARKS;
3747                                                                 else if (!strcasecmp(parameter[1], "nomipmaps"))
3748                                                                         flags |= Q3SURFACEPARM_NOMIPMAPS;
3749                                                                 else if (!strcasecmp(parameter[1], "nonsolid"))
3750                                                                         flags |= Q3SURFACEPARM_NONSOLID;
3751                                                                 else if (!strcasecmp(parameter[1], "origin"))
3752                                                                         flags |= Q3SURFACEPARM_ORIGIN;
3753                                                                 else if (!strcasecmp(parameter[1], "playerclip"))
3754                                                                         flags |= Q3SURFACEPARM_PLAYERCLIP;
3755                                                                 else if (!strcasecmp(parameter[1], "sky"))
3756                                                                         flags |= Q3SURFACEPARM_SKY;
3757                                                                 else if (!strcasecmp(parameter[1], "slick"))
3758                                                                         flags |= Q3SURFACEPARM_SLICK;
3759                                                                 else if (!strcasecmp(parameter[1], "slime"))
3760                                                                         flags |= Q3SURFACEPARM_SLIME;
3761                                                                 else if (!strcasecmp(parameter[1], "structural"))
3762                                                                         flags |= Q3SURFACEPARM_STRUCTURAL;
3763                                                                 else if (!strcasecmp(parameter[1], "trans"))
3764                                                                         flags |= Q3SURFACEPARM_TRANS;
3765                                                                 else if (!strcasecmp(parameter[1], "water"))
3766                                                                         flags |= Q3SURFACEPARM_WATER;
3767                                                                 else
3768                                                                         Con_Printf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[i], parameter[1]);
3769                                                         }
3770                                                         else if (!strcasecmp(parameter[0], "sky") && numparameters >= 2)
3771                                                                 strlcpy(sky, parameter[1], sizeof(sky));
3772                                                         else if (!strcasecmp(parameter[0], "skyparms") && numparameters >= 2)
3773                                                         {
3774                                                                 if (!atoi(parameter[1]) && strcasecmp(parameter[1], "-"))
3775                                                                         strlcpy(sky, parameter[1], sizeof(sky));
3776                                                         }
3777                                                         else if (!strcasecmp(parameter[0], "cull") && numparameters >= 2)
3778                                                         {
3779                                                                 if (!strcasecmp(parameter[1], "disable") || !strcasecmp(parameter[1], "none") || !strcasecmp(parameter[1], "twosided"))
3780                                                                         flags2 |= Q3TEXTUREFLAG_TWOSIDED;
3781                                                         }
3782                                                         else if (!strcasecmp(parameter[0], "nomipmaps"))
3783                                                                 flags2 |= Q3TEXTUREFLAG_NOMIPMAPS;
3784                                                         else if (!strcasecmp(parameter[0], "nopicmip"))
3785                                                                 flags2 |= Q3TEXTUREFLAG_NOPICMIP;
3786                                                         else if (!strcasecmp(parameter[0], "deformvertexes") && numparameters >= 2)
3787                                                         {
3788                                                                 if (!strcasecmp(parameter[1], "autosprite") && numparameters == 2)
3789                                                                         flags2 |= Q3TEXTUREFLAG_AUTOSPRITE;
3790                                                                 if (!strcasecmp(parameter[1], "autosprite2") && numparameters == 2)
3791                                                                         flags2 |= Q3TEXTUREFLAG_AUTOSPRITE2;
3792                                                         }
3793                                                 }
3794                                                 // force transparent render path for a number of odd
3795                                                 // shader effects to avoid bogging down the normal
3796                                                 // render path unnecessarily
3797                                                 if (flags2 & (Q3TEXTUREFLAG_ADDITIVE | Q3TEXTUREFLAG_AUTOSPRITE | Q3TEXTUREFLAG_AUTOSPRITE2))
3798                                                         flags |= Q3SURFACEPARM_TRANS;
3799                                                 // add shader to list (shadername and flags)
3800                                                 // actually here we just poke into the texture settings
3801                                                 for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++)
3802                                                 {
3803                                                         if (!strcasecmp(out->name, shadername))
3804                                                         {
3805                                                                 out->surfaceparms = flags;
3806                                                                 out->textureflags = flags2;
3807                                                                 strlcpy(out->firstpasstexturename, firstpasstexturename, sizeof(out->firstpasstexturename));
3808                                                                 if ((flags & Q3SURFACEPARM_SKY) && sky[0])
3809                                                                 {
3810                                                                         // quake3 seems to append a _ to the skybox name, so this must do so as well
3811                                                                         snprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", sky);
3812                                                                 }
3813                                                         }
3814                                                 }
3815                                         }
3816                                         else
3817                                         {
3818                                                 Con_Printf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[i], com_token);
3819                                                 goto parseerror;
3820                                         }
3821                                 }
3822 parseerror:
3823                                 Mem_Free(f);
3824                         }
3825                 }
3826         }
3827
3828         c = 0;
3829         for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++)
3830         {
3831                 if (out->surfaceparms == -1)
3832                 {
3833                         c++;
3834                         Con_DPrintf("%s: No shader found for texture \"%s\"\n", loadmodel->name, out->name);
3835                         out->surfaceparms = 0;
3836                         // these are defaults
3837                         if (!strncmp(out->name, "textures/skies/", 15))
3838                                 out->surfaceparms |= Q3SURFACEPARM_SKY;
3839                         //if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk")
3840                         // || !strcmp(out->name, "nodraw") || !strcmp(out->name, "common/nodraw") || !strcmp(out->name, "textures/common/nodraw"))
3841                         //      out->surfaceparms |= Q3SURFACEPARM_NODRAW;
3842                         //if (R_TextureHasAlpha(out->skin.base))
3843                         //      out->surfaceparms |= Q3SURFACEPARM_TRANS;
3844                 }
3845                 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))
3846                         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);
3847         }
3848         Con_DPrintf("%s: %i textures missing shaders\n", loadmodel->name, c);
3849 }
3850
3851 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3852 {
3853         q3dplane_t *in;
3854         mplane_t *out;
3855         int i, count;
3856
3857         in = (void *)(mod_base + l->fileofs);
3858         if (l->filelen % sizeof(*in))
3859                 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3860         count = l->filelen / sizeof(*in);
3861         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3862
3863         loadmodel->brushq3.data_planes = out;
3864         loadmodel->brushq3.num_planes = count;
3865
3866         for (i = 0;i < count;i++, in++, out++)
3867         {
3868                 out->normal[0] = LittleLong(in->normal[0]);
3869                 out->normal[1] = LittleLong(in->normal[1]);
3870                 out->normal[2] = LittleLong(in->normal[2]);
3871                 out->dist = LittleLong(in->dist);
3872                 PlaneClassify(out);
3873         }
3874 }
3875
3876 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3877 {
3878         q3dbrushside_t *in;
3879         q3mbrushside_t *out;
3880         int i, n, count;
3881
3882         in = (void *)(mod_base + l->fileofs);
3883         if (l->filelen % sizeof(*in))
3884                 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3885         count = l->filelen / sizeof(*in);
3886         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3887
3888         loadmodel->brushq3.data_brushsides = out;
3889         loadmodel->brushq3.num_brushsides = count;
3890
3891         for (i = 0;i < count;i++, in++, out++)
3892         {
3893                 n = LittleLong(in->planeindex);
3894                 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3895                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3896                 out->plane = loadmodel->brushq3.data_planes + n;
3897                 n = LittleLong(in->textureindex);
3898                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3899                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3900                 out->texture = loadmodel->brushq3.data_textures + n;
3901         }
3902 }
3903
3904 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3905 {
3906         q3dbrush_t *in;
3907         q3mbrush_t *out;
3908         int i, j, n, c, count, maxplanes;
3909         mplane_t *planes;
3910
3911         in = (void *)(mod_base + l->fileofs);
3912         if (l->filelen % sizeof(*in))
3913                 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3914         count = l->filelen / sizeof(*in);
3915         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3916
3917         loadmodel->brushq3.data_brushes = out;
3918         loadmodel->brushq3.num_brushes = count;
3919
3920         maxplanes = 0;
3921         planes = NULL;
3922
3923         for (i = 0;i < count;i++, in++, out++)
3924         {
3925                 n = LittleLong(in->firstbrushside);
3926                 c = LittleLong(in->numbrushsides);
3927                 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3928                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3929                 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3930                 out->numbrushsides = c;
3931                 n = LittleLong(in->textureindex);
3932                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3933                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3934                 out->texture = loadmodel->brushq3.data_textures + n;
3935
3936                 // make a list of mplane_t structs to construct a colbrush from
3937                 if (maxplanes < out->numbrushsides)
3938                 {
3939                         maxplanes = out->numbrushsides;
3940                         if (planes)
3941                                 Mem_Free(planes);
3942                         planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
3943                 }
3944                 for (j = 0;j < out->numbrushsides;j++)
3945                 {
3946                         VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
3947                         planes[j].dist = out->firstbrushside[j].plane->dist;
3948                 }
3949                 // make the colbrush from the planes
3950                 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents);
3951         }
3952         if (planes)
3953                 Mem_Free(planes);
3954 }
3955
3956 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3957 {
3958         q3deffect_t *in;
3959         q3meffect_t *out;
3960         int i, n, count;
3961
3962         in = (void *)(mod_base + l->fileofs);
3963         if (l->filelen % sizeof(*in))
3964                 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3965         count = l->filelen / sizeof(*in);
3966         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3967
3968         loadmodel->brushq3.data_effects = out;
3969         loadmodel->brushq3.num_effects = count;
3970
3971         for (i = 0;i < count;i++, in++, out++)
3972         {
3973                 strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
3974                 n = LittleLong(in->brushindex);
3975                 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3976                         Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3977                 out->brush = loadmodel->brushq3.data_brushes + n;
3978                 out->unknown = LittleLong(in->unknown);
3979         }
3980 }
3981
3982 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3983 {
3984         q3dvertex_t *in;
3985         int i, count;
3986
3987         in = (void *)(mod_base + l->fileofs);
3988         if (l->filelen % sizeof(*in))
3989                 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3990         loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3991         loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)));
3992         loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + count * 3;
3993         loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + count * 2;
3994         loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + count * 2;
3995         loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + count * 3;
3996         loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + count * 3;
3997         loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + count * 3;
3998
3999         for (i = 0;i < count;i++, in++)
4000         {
4001                 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
4002                 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
4003                 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
4004                 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
4005                 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
4006                 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
4007                 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
4008                 // svector/tvector are calculated later in face loading
4009                 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
4010                 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
4011                 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
4012                 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
4013                 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
4014                 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
4015                 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
4016                 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
4017                 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
4018                 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
4019                 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
4020                 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
4021                 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
4022         }
4023 }
4024
4025 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
4026 {
4027         int *in;
4028         int *out;
4029         int i, count;
4030
4031         in = (void *)(mod_base + l->fileofs);
4032         if (l->filelen % sizeof(int[3]))
4033                 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
4034         count = l->filelen / sizeof(*in);
4035         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out) * 2);
4036
4037         loadmodel->brushq3.num_triangles = count / 3;
4038         loadmodel->brushq3.data_element3i = out;
4039         loadmodel->brushq3.data_neighbor3i = out + count;
4040
4041         for (i = 0;i < count;i++, in++, out++)
4042         {
4043                 *out = LittleLong(*in);
4044                 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
4045                 {
4046                         Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
4047                         *out = 0;
4048                 }
4049         }
4050 }
4051
4052 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
4053 {
4054         q3dlightmap_t *in;
4055         rtexture_t **out;
4056         int i, count;
4057
4058         if (!l->filelen)
4059                 return;
4060         in = (void *)(mod_base + l->fileofs);
4061         if (l->filelen % sizeof(*in))
4062                 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
4063         count = l->filelen / sizeof(*in);
4064         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4065
4066         loadmodel->brushq3.data_lightmaps = out;
4067         loadmodel->brushq3.num_lightmaps = count;
4068
4069         for (i = 0;i < count;i++, in++, out++)
4070                 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
4071 }
4072
4073 static void Mod_Q3BSP_LoadFaces(lump_t *l)
4074 {
4075         q3dface_t *in;
4076         q3msurface_t *out;
4077         int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles, firstvertex, firstelement, type;
4078         //int *originalelement3i;
4079         //int *originalneighbor3i;
4080         float *originalvertex3f;
4081         //float *originalsvector3f;
4082         //float *originaltvector3f;
4083         //float *originalnormal3f;
4084         float *originalcolor4f;
4085         float *originaltexcoordtexture2f;
4086         float *originaltexcoordlightmap2f;
4087         float *v;
4088
4089         in = (void *)(mod_base + l->fileofs);
4090         if (l->filelen % sizeof(*in))
4091                 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
4092         count = l->filelen / sizeof(*in);
4093         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4094
4095         loadmodel->brushq3.data_faces = out;
4096         loadmodel->brushq3.num_faces = count;
4097
4098         for (i = 0;i < count;i++, in++, out++)
4099         {
4100                 // check face type first
4101                 type = LittleLong(in->type);
4102                 if (type != Q3FACETYPE_POLYGON
4103                  && type != Q3FACETYPE_PATCH
4104                  && type != Q3FACETYPE_MESH
4105                  && type != Q3FACETYPE_FLARE)
4106                 {
4107                         Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, type);
4108                         out->num_vertices = 0;
4109                         out->num_triangles = 0;
4110                         type = 0; // error
4111                         continue;
4112                 }
4113
4114                 n = LittleLong(in->textureindex);
4115                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
4116                 {
4117                         Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
4118                         out->num_vertices = 0;
4119                         out->num_triangles = 0;
4120                         type = 0; // error
4121                         continue;
4122                         n = 0;
4123                 }
4124                 out->texture = loadmodel->brushq3.data_textures + n;
4125                 n = LittleLong(in->effectindex);
4126                 if (n < -1 || n >= loadmodel->brushq3.num_effects)
4127                 {
4128                         Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
4129                         n = -1;
4130                 }
4131                 if (n == -1)
4132                         out->effect = NULL;
4133                 else
4134                         out->effect = loadmodel->brushq3.data_effects + n;
4135                 n = LittleLong(in->lightmapindex);
4136                 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
4137                 {
4138                         Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
4139                         n = -1;
4140                 }
4141                 if (n == -1)
4142                         out->lightmaptexture = NULL;
4143                 else
4144                         out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
4145
4146                 firstvertex = LittleLong(in->firstvertex);
4147                 out->num_vertices = LittleLong(in->numvertices);
4148                 firstelement = LittleLong(in->firstelement);
4149                 out->num_triangles = LittleLong(in->numelements) / 3;
4150                 if (out->num_triangles * 3 != LittleLong(in->numelements))
4151                 {
4152                         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));
4153                         out->num_vertices = 0;
4154                         out->num_triangles = 0;
4155                         type = 0; // error
4156                         continue;
4157                 }
4158                 if (firstvertex < 0 || firstvertex + out->num_vertices > loadmodel->brushq3.num_vertices)
4159                 {
4160                         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);
4161                         out->num_vertices = 0;
4162                         out->num_triangles = 0;
4163                         type = 0; // error
4164                         continue;
4165                 }
4166                 if (firstelement < 0 || firstelement + out->num_triangles * 3 > loadmodel->brushq3.num_triangles * 3)
4167                 {
4168                         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);
4169                         out->num_vertices = 0;
4170                         out->num_triangles = 0;
4171                         type = 0; // error
4172                         continue;
4173                 }
4174                 switch(type)
4175                 {
4176                 case Q3FACETYPE_POLYGON:
4177                 case Q3FACETYPE_MESH:
4178                         // no processing necessary
4179                         out->data_vertex3f = loadmodel->brushq3.data_vertex3f + firstvertex * 3;
4180                         out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + firstvertex * 2;
4181                         out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + firstvertex * 2;
4182                         out->data_svector3f = loadmodel->brushq3.data_svector3f + firstvertex * 3;
4183                         out->data_tvector3f = loadmodel->brushq3.data_tvector3f + firstvertex * 3;
4184                         out->data_normal3f = loadmodel->brushq3.data_normal3f + firstvertex * 3;
4185                         out->data_color4f = loadmodel->brushq3.data_color4f + firstvertex * 4;
4186                         out->data_element3i = loadmodel->brushq3.data_element3i + firstelement;
4187                         out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + firstelement;
4188                         break;
4189                 case Q3FACETYPE_PATCH:
4190                         patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
4191                         patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
4192                         if (patchsize[0] < 1 || patchsize[1] < 1)
4193                         {
4194                                 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
4195                                 out->num_vertices = 0;
4196                                 out->num_triangles = 0;
4197                                 type = 0; // error
4198                                 continue;
4199                         }
4200                         originalvertex3f = loadmodel->brushq3.data_vertex3f + firstvertex * 3;
4201                         //originalsvector3f = loadmodel->brushq3.data_svector3f + firstvertex * 3;
4202                         //originaltvector3f = loadmodel->brushq3.data_tvector3f + firstvertex * 3;
4203                         //originalnormal3f = loadmodel->brushq3.data_normal3f + firstvertex * 3;
4204                         originaltexcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + firstvertex * 2;
4205                         originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + firstvertex * 2;
4206                         originalcolor4f = loadmodel->brushq3.data_color4f + firstvertex * 4;
4207                         //originalelement3i = loadmodel->brushq3.data_element3i + firstelement;
4208                         //originalneighbor3i = loadmodel->brushq3.data_neighbor3i + firstelement;
4209                         /*
4210                         originalvertex3f = out->data_vertex3f;
4211                         //originalsvector3f = out->data_svector3f;
4212                         //originaltvector3f = out->data_tvector3f;
4213                         //originalnormal3f = out->data_normal3f;
4214                         originalcolor4f = out->data_color4f;
4215                         originaltexcoordtexture2f = out->data_texcoordtexture2f;
4216                         originaltexcoordlightmap2f = out->data_texcoordlightmap2f;
4217                         //originalelement3i = out->data_element3i;
4218                         //originalneighbor3i = out->data_neighbor3i;
4219                         */
4220                         // convert patch to Q3FACETYPE_MESH
4221                         xlevel = QuadraticBSplinePatchSubdivisionLevelOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value, 10);
4222                         ylevel = QuadraticBSplinePatchSubdivisionLevelOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value, 10);
4223                         // bound to user settings
4224                         xlevel = bound(r_subdivisions_minlevel.integer, xlevel, r_subdivisions_maxlevel.integer);
4225                         ylevel = bound(r_subdivisions_minlevel.integer, ylevel, r_subdivisions_maxlevel.integer);
4226                         // bound to sanity settings
4227                         xlevel = bound(0, xlevel, 10);
4228                         ylevel = bound(0, ylevel, 10);
4229                         // bound to user limit on vertices
4230                         while ((xlevel > 0 || ylevel > 0) && (((patchsize[0] - 1) << xlevel) + 1) * (((patchsize[1] - 1) << ylevel) + 1) > min(r_subdivisions_maxvertices.integer, 262144))
4231                         {
4232                                 if (xlevel > ylevel)
4233                                         xlevel--;
4234                                 else
4235                                         ylevel--;
4236                         }
4237                         finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
4238                         finalheight = ((patchsize[1] - 1) << ylevel) + 1;
4239                         finalvertices = finalwidth * finalheight;
4240                         finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
4241                         out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices);
4242                         out->data_svector3f = out->data_vertex3f + finalvertices * 3;
4243                         out->data_tvector3f = out->data_svector3f + finalvertices * 3;
4244                         out->data_normal3f = out->data_tvector3f + finalvertices * 3;
4245                         out->data_color4f = out->data_normal3f + finalvertices * 3;
4246                         out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4;
4247                         out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2;
4248                         out->data_element3i = Mem_Alloc(loadmodel->mempool, sizeof(int[6]) * finaltriangles);
4249                         out->data_neighbor3i = out->data_element3i + finaltriangles * 3;
4250                         type = Q3FACETYPE_MESH;
4251                         firstvertex = -1;
4252                         out->num_vertices = finalvertices;
4253                         firstelement = -1;
4254                         out->num_triangles = finaltriangles;
4255                         // generate geometry
4256                         // (note: normals are skipped because they get recalculated)
4257                         QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f);
4258                         QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f);
4259                         QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f);
4260                         QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f);
4261                         // generate elements
4262                         e = out->data_element3i;
4263                         for (y = 0;y < finalheight - 1;y++)
4264                         {
4265                                 row0 = (y + 0) * finalwidth;
4266                                 row1 = (y + 1) * finalwidth;
4267                                 for (x = 0;x < finalwidth - 1;x++)
4268                                 {
4269                                         *e++ = row0;
4270                                         *e++ = row1;
4271                                         *e++ = row0 + 1;
4272                                         *e++ = row1;
4273                                         *e++ = row1 + 1;
4274                                         *e++ = row0 + 1;
4275                                         row0++;
4276                                         row1++;
4277                                 }
4278                         }
4279                         out->num_triangles = Mod_RemoveDegenerateTriangles(out->num_triangles, out->data_element3i, out->data_element3i, out->data_vertex3f);
4280                         if (developer.integer)
4281                         {
4282                                 if (out->num_triangles < finaltriangles)
4283                                         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);
4284                                 else
4285                                         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);
4286                         }
4287                         // q3map does not put in collision brushes for curves... ugh
4288                         // build the lower quality collision geometry
4289                         xlevel = QuadraticBSplinePatchSubdivisionLevelOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value, 10);
4290                         ylevel = QuadraticBSplinePatchSubdivisionLevelOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value, 10);
4291                         // bound to user settings
4292                         xlevel = bound(r_subdivisions_collision_minlevel.integer, xlevel, r_subdivisions_collision_maxlevel.integer);
4293                         ylevel = bound(r_subdivisions_collision_minlevel.integer, ylevel, r_subdivisions_collision_maxlevel.integer);
4294                         // bound to sanity settings
4295                         xlevel = bound(0, xlevel, 10);
4296                         ylevel = bound(0, ylevel, 10);
4297                         // bound to user limit on vertices
4298                         while ((xlevel > 0 || ylevel > 0) && (((patchsize[0] - 1) << xlevel) + 1) * (((patchsize[1] - 1) << ylevel) + 1) > min(r_subdivisions_collision_maxvertices.integer, 262144))
4299                         {
4300                                 if (xlevel > ylevel)
4301                                         xlevel--;
4302                                 else
4303                                         ylevel--;
4304                         }
4305                         finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
4306                         finalheight = ((patchsize[1] - 1) << ylevel) + 1;
4307                         finalvertices = finalwidth * finalheight;
4308                         finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
4309                         out->data_collisionvertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * finalvertices);
4310                         out->data_collisionelement3i = Mem_Alloc(loadmodel->mempool, sizeof(int[3]) * finaltriangles);
4311                         out->num_collisionvertices = finalvertices;
4312                         out->num_collisiontriangles = finaltriangles;
4313                         QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_collisionvertex3f);
4314                         // generate elements
4315                         e = out->data_collisionelement3i;
4316                         for (y = 0;y < finalheight - 1;y++)
4317                         {
4318                                 row0 = (y + 0) * finalwidth;
4319                                 row1 = (y + 1) * finalwidth;
4320                                 for (x = 0;x < finalwidth - 1;x++)
4321                                 {
4322                                         *e++ = row0;
4323                                         *e++ = row1;
4324                                         *e++ = row0 + 1;
4325                                         *e++ = row1;
4326                                         *e++ = row1 + 1;
4327                                         *e++ = row0 + 1;
4328                                         row0++;
4329                                         row1++;
4330                                 }
4331                         }
4332                         out->num_collisiontriangles = Mod_RemoveDegenerateTriangles(out->num_collisiontriangles, out->data_collisionelement3i, out->data_collisionelement3i, out->data_collisionvertex3f);
4333                         if (developer.integer)
4334                         {
4335                                 if (out->num_collisiontriangles < finaltriangles)
4336                                         Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided for collisions to %i vertices / %i triangles, %i degenerate triangles removed (leaving %i)\n", patchsize[0], patchsize[1], out->num_collisionvertices, finaltriangles, finaltriangles - out->num_collisiontriangles, out->num_collisiontriangles);
4337                                 else
4338                                         Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided for collisions to %i vertices / %i triangles\n", patchsize[0], patchsize[1], out->num_collisionvertices, out->num_collisiontriangles);
4339                         }
4340                         break;
4341                 case Q3FACETYPE_FLARE:
4342                         Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
4343                         // don't render it
4344                         out->num_vertices = 0;
4345                         out->num_triangles = 0;
4346                         type = 0;
4347                         break;
4348                 }
4349                 for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++)
4350                         if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
4351                                 invalidelements++;
4352                 if (invalidelements)
4353                 {
4354                         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);
4355                         for (j = 0;j < out->num_triangles * 3;j++)
4356                         {
4357                                 Con_Printf(" %i", out->data_element3i[j]);
4358                                 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
4359                                         out->data_element3i[j] = 0;
4360                         }
4361                         Con_Print("\n");
4362                 }
4363                 // for shadow volumes
4364                 Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->num_triangles);
4365                 // for per pixel lighting
4366                 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);
4367                 // calculate a bounding box
4368                 VectorClear(out->mins);
4369                 VectorClear(out->maxs);
4370                 if (out->num_vertices)
4371                 {
4372                         VectorCopy(out->data_vertex3f, out->mins);
4373                         VectorCopy(out->data_vertex3f, out->maxs);
4374                         for (j = 1, v = out->data_vertex3f + 3;j < out->num_vertices;j++, v += 3)
4375                         {
4376                                 out->mins[0] = min(out->mins[0], v[0]);
4377                                 out->maxs[0] = max(out->maxs[0], v[0]);
4378                                 out->mins[1] = min(out->mins[1], v[1]);
4379                                 out->maxs[1] = max(out->maxs[1], v[1]);
4380                                 out->mins[2] = min(out->mins[2], v[2]);
4381                                 out->maxs[2] = max(out->maxs[2], v[2]);
4382                         }
4383                         out->mins[0] -= 1.0f;
4384                         out->mins[1] -= 1.0f;
4385                         out->mins[2] -= 1.0f;
4386                         out->maxs[0] += 1.0f;
4387                         out->maxs[1] += 1.0f;
4388                         out->maxs[2] += 1.0f;
4389                 }
4390         }
4391
4392         // LordHavoc: experimental array merger (disabled because it wastes time and uses 2x memory while merging)
4393         /*
4394         {
4395         int totalverts, totaltris;
4396         int originalnum_vertices;
4397         float *originaldata_vertex3f;
4398         float *originaldata_texcoordtexture2f;
4399         float *originaldata_texcoordlightmap2f;
4400         float *originaldata_svector3f;
4401         float *originaldata_tvector3f;
4402         float *originaldata_normal3f;
4403         float *originaldata_color4f;
4404         int originalnum_triangles;
4405         int *originaldata_element3i;
4406         int *originaldata_neighbor3i;
4407
4408         totalverts = 0;
4409         totaltris = 0;
4410         for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
4411         {
4412                 if (!out->type)
4413                         continue;
4414                 totalverts += out->num_vertices;
4415                 totaltris += out->num_triangles;
4416         }
4417
4418         originalnum_vertices = loadmodel->brushq3.num_vertices;
4419         originaldata_vertex3f = loadmodel->brushq3.data_vertex3f;
4420         originaldata_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f;
4421         originaldata_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f;
4422         originaldata_svector3f = loadmodel->brushq3.data_svector3f;
4423         originaldata_tvector3f = loadmodel->brushq3.data_tvector3f;
4424         originaldata_normal3f = loadmodel->brushq3.data_normal3f;
4425         originaldata_color4f = loadmodel->brushq3.data_color4f;
4426         originalnum_triangles = loadmodel->brushq3.num_triangles;
4427         originaldata_element3i = loadmodel->brushq3.data_element3i;
4428         originaldata_neighbor3i = loadmodel->brushq3.data_neighbor3i;
4429         loadmodel->brushq3.num_vertices = totalverts;
4430         loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, totalverts * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)) + totaltris * (sizeof(int) * (3 * 2)));
4431         loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + totalverts * 3;
4432         loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2;
4433         loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2;
4434         loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + totalverts * 3;
4435         loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + totalverts * 3;
4436         loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + totalverts * 3;
4437         loadmodel->brushq3.num_triangles = totaltris;
4438         loadmodel->brushq3.data_element3i = (int *)(loadmodel->brushq3.data_color4f + totalverts * 4);
4439         loadmodel->brushq3.data_neighbor3i = loadmodel->brushq3.data_element3i + totaltris * 3;
4440         totalverts = 0;
4441         totaltris = 0;
4442         for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
4443         {
4444                 if (!out->type)
4445                         continue;
4446                 Con_Printf("totalverts %i, totaltris %i\n", totalverts, totaltris);
4447                 memcpy(loadmodel->brushq3.data_vertex3f + totalverts * 3, out->data_vertex3f, out->num_vertices * 3 * sizeof(float));
4448                 memcpy(loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2, out->data_texcoordtexture2f, out->num_vertices * 2 * sizeof(float));
4449                 memcpy(loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2, out->data_texcoordlightmap2f, out->num_vertices * 2 * sizeof(float));
4450                 memcpy(loadmodel->brushq3.data_svector3f + totalverts * 3, out->data_svector3f, out->num_vertices * 3 * sizeof(float));
4451                 memcpy(loadmodel->brushq3.data_tvector3f + totalverts * 3, out->data_tvector3f, out->num_vertices * 3 * sizeof(float));
4452                 memcpy(loadmodel->brushq3.data_normal3f + totalverts * 3, out->data_normal3f, out->num_vertices * 3 * sizeof(float));
4453                 memcpy(loadmodel->brushq3.data_color4f + totalverts * 4, out->data_color4f, out->num_vertices * 4 * sizeof(float));
4454                 memcpy(loadmodel->brushq3.data_element3i + totaltris * 3, out->data_element3i, out->num_triangles * 3 * sizeof(int));
4455                 memcpy(loadmodel->brushq3.data_neighbor3i + totaltris * 3, out->data_neighbor3i, out->num_triangles * 3 * sizeof(int));
4456                 if (out->firstvertex == -1)
4457                         Mem_Free(out->data_vertex3f);
4458                 if (out->firstelement == -1)
4459                         Mem_Free(out->data_element3i);
4460                 out->firstvertex = totalverts;
4461                 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
4462                 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
4463                 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
4464                 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
4465                 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
4466                 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
4467                 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
4468                 out->firstelement = totaltris * 3;
4469                 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
4470                 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
4471                 //for (j = 0;j < out->numtriangles * 3;j++)
4472                 //      out->data_element3i[j] += totalverts - out->firstvertex;
4473                 totalverts += out->num_vertices;
4474                 totaltris += out->num_triangles;
4475         }
4476         Mem_Free(originaldata_vertex3f);
4477         Mem_Free(originaldata_element3i);
4478         }
4479         */
4480 }
4481
4482 static void Mod_Q3BSP_LoadModels(lump_t *l)
4483 {
4484         q3dmodel_t *in;
4485         q3mmodel_t *out;
4486         int i, j, n, c, count;
4487
4488         in = (void *)(mod_base + l->fileofs);
4489         if (l->filelen % sizeof(*in))
4490                 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
4491         count = l->filelen / sizeof(*in);
4492         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4493
4494         loadmodel->brushq3.data_models = out;
4495         loadmodel->brushq3.num_models = count;
4496
4497         for (i = 0;i < count;i++, in++, out++)
4498         {
4499                 for (j = 0;j < 3;j++)
4500                 {
4501                         out->mins[j] = LittleFloat(in->mins[j]);
4502                         out->maxs[j] = LittleFloat(in->maxs[j]);
4503                 }
4504                 n = LittleLong(in->firstface);
4505                 c = LittleLong(in->numfaces);
4506                 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
4507                         Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
4508                 out->firstface = loadmodel->brushq3.data_faces + n;
4509                 out->numfaces = c;
4510                 n = LittleLong(in->firstbrush);
4511                 c = LittleLong(in->numbrushes);
4512                 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
4513                         Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
4514                 out->firstbrush = loadmodel->brushq3.data_brushes + n;
4515                 out->numbrushes = c;
4516         }
4517 }
4518
4519 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
4520 {
4521         int *in;
4522         q3mbrush_t **out;
4523         int i, n, count;
4524
4525         in = (void *)(mod_base + l->fileofs);
4526         if (l->filelen % sizeof(*in))
4527                 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
4528         count = l->filelen / sizeof(*in);
4529         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4530
4531         loadmodel->brushq3.data_leafbrushes = out;
4532         loadmodel->brushq3.num_leafbrushes = count;
4533
4534         for (i = 0;i < count;i++, in++, out++)
4535         {
4536                 n = LittleLong(*in);
4537                 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
4538                         Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
4539                 *out = loadmodel->brushq3.data_brushes + n;
4540         }
4541 }
4542
4543 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
4544 {
4545         int *in;
4546         q3msurface_t **out;
4547         int i, n, count;
4548
4549         in = (void *)(mod_base + l->fileofs);
4550         if (l->filelen % sizeof(*in))
4551                 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
4552         count = l->filelen / sizeof(*in);
4553         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4554
4555         loadmodel->brushq3.data_leaffaces = out;
4556         loadmodel->brushq3.num_leaffaces = count;
4557
4558         loadmodel->brushq3.data_leaffacenums = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
4559
4560         for (i = 0;i < count;i++, in++, out++)
4561         {
4562                 n = LittleLong(*in);
4563                 if (n < 0 || n >= loadmodel->brushq3.num_faces)
4564                         Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
4565                 *out = loadmodel->brushq3.data_faces + n;
4566                 loadmodel->brushq3.data_leaffacenums[i] = n;
4567         }
4568 }
4569
4570 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
4571 {
4572         q3dleaf_t *in;
4573         q3mleaf_t *out;
4574         int i, j, n, c, count;
4575
4576         in = (void *)(mod_base + l->fileofs);
4577         if (l->filelen % sizeof(*in))
4578                 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
4579         count = l->filelen / sizeof(*in);
4580         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4581
4582         loadmodel->brushq3.data_leafs = out;
4583         loadmodel->brushq3.num_leafs = count;
4584
4585         for (i = 0;i < count;i++, in++, out++)
4586         {
4587                 out->parent = NULL;
4588                 out->plane = NULL;
4589                 out->clusterindex = LittleLong(in->clusterindex);
4590                 out->areaindex = LittleLong(in->areaindex);
4591                 for (j = 0;j < 3;j++)
4592                 {
4593                         // yes the mins/maxs are ints
4594                         out->mins[j] = LittleLong(in->mins[j]) - 1;
4595                         out->maxs[j] = LittleLong(in->maxs[j]) + 1;
4596                 }
4597                 n = LittleLong(in->firstleafface);
4598                 c = LittleLong(in->numleaffaces);
4599                 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
4600                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
4601                 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
4602                 out->firstleaffacenum = loadmodel->brushq3.data_leaffacenums + n;
4603                 out->numleaffaces = c;
4604                 n = LittleLong(in->firstleafbrush);
4605                 c = LittleLong(in->numleafbrushes);
4606                 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
4607                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
4608                 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
4609                 out->numleafbrushes = c;
4610         }
4611 }
4612
4613 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
4614 {
4615         if (node->parent)
4616                 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
4617         node->parent = parent;
4618         if (node->plane)
4619         {
4620                 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
4621                 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
4622         }
4623 }
4624
4625 static void Mod_Q3BSP_LoadNodes(lump_t *l)
4626 {
4627         q3dnode_t *in;
4628         q3mnode_t *out;
4629         int i, j, n, count;
4630
4631         in = (void *)(mod_base + l->fileofs);
4632         if (l->filelen % sizeof(*in))
4633                 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
4634         count = l->filelen / sizeof(*in);
4635         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4636
4637         loadmodel->brushq3.data_nodes = out;
4638         loadmodel->brushq3.num_nodes = count;
4639
4640         for (i = 0;i < count;i++, in++, out++)
4641         {
4642                 out->parent = NULL;
4643                 n = LittleLong(in->planeindex);
4644                 if (n < 0 || n >= loadmodel->brushq3.num_planes)
4645                         Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
4646                 out->plane = loadmodel->brushq3.data_planes + n;
4647                 for (j = 0;j < 2;j++)
4648                 {
4649                         n = LittleLong(in->childrenindex[j]);
4650                         if (n >= 0)
4651                         {
4652                                 if (n >= loadmodel->brushq3.num_nodes)
4653                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
4654                                 out->children[j] = loadmodel->brushq3.data_nodes + n;
4655                         }
4656                         else
4657                         {
4658                                 n = -1 - n;
4659                                 if (n >= loadmodel->brushq3.num_leafs)
4660                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
4661                                 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
4662                         }
4663                 }
4664                 for (j = 0;j < 3;j++)
4665                 {
4666                         // yes the mins/maxs are ints
4667                         out->mins[j] = LittleLong(in->mins[j]) - 1;
4668                         out->maxs[j] = LittleLong(in->maxs[j]) + 1;
4669                 }
4670         }
4671
4672         // set the parent pointers
4673         Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
4674 }
4675
4676 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
4677 {
4678         q3dlightgrid_t *in;
4679         q3dlightgrid_t *out;
4680         int count;
4681
4682         in = (void *)(mod_base + l->fileofs);
4683         if (l->filelen % sizeof(*in))
4684                 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
4685         loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
4686         loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
4687         loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
4688         loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4689         loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4690         loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4691         loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4692         loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4693         loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4694         loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4695         loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4696         loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4697         count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4698         if (l->filelen)
4699         {
4700                 if (l->filelen < count * (int)sizeof(*in))
4701                         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]);
4702                 if (l->filelen != count * (int)sizeof(*in))
4703                         Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4704         }
4705
4706         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4707         loadmodel->brushq3.data_lightgrid = out;
4708         loadmodel->brushq3.num_lightgrid = count;
4709
4710         // no swapping or validation necessary
4711         if (l->filelen)
4712                 memcpy(out, in, count * (int)sizeof(*out));
4713         else
4714         {
4715                 // no data, fill with white
4716                 int i;
4717                 for (i = 0;i < count;i++)
4718                 {
4719                         out[i].ambientrgb[0] = 128;
4720                         out[i].ambientrgb[1] = 128;
4721                         out[i].ambientrgb[2] = 128;
4722                         out[i].diffusergb[0] = 0;
4723                         out[i].diffusergb[1] = 0;
4724                         out[i].diffusergb[2] = 0;
4725                         out[i].diffusepitch = 0;
4726                         out[i].diffuseyaw = 0;
4727                 }
4728         }
4729
4730         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]);
4731         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]);
4732 }
4733
4734 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4735 {
4736         q3dpvs_t *in;
4737         int totalchains;
4738
4739         if (l->filelen == 0)
4740         {
4741                 int i;
4742                 // unvised maps often have cluster indices even without pvs, so check
4743                 // leafs to find real number of clusters
4744                 loadmodel->brush.num_pvsclusters = 1;
4745                 for (i = 0;i < loadmodel->brushq3.num_leafs;i++)
4746                         loadmodel->brush.num_pvsclusters = min(loadmodel->brush.num_pvsclusters, loadmodel->brushq3.data_leafs[i].clusterindex + 1);
4747
4748                 // create clusters
4749                 loadmodel->brush.num_pvsclusterbytes = (loadmodel->brush.num_pvsclusters + 7) / 8;
4750                 totalchains = loadmodel->brush.num_pvsclusterbytes * loadmodel->brush.num_pvsclusters;
4751                 loadmodel->brush.data_pvsclusters = Mem_Alloc(loadmodel->mempool, totalchains);
4752                 memset(loadmodel->brush.data_pvsclusters, 0xFF, totalchains);
4753                 return;
4754         }
4755
4756         in = (void *)(mod_base + l->fileofs);
4757         if (l->filelen < 9)
4758                 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4759
4760         loadmodel->brush.num_pvsclusters = LittleLong(in->numclusters);
4761         loadmodel->brush.num_pvsclusterbytes = LittleLong(in->chainlength);
4762         if (loadmodel->brush.num_pvsclusterbytes < ((loadmodel->brush.num_pvsclusters + 7) / 8))
4763                 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brush.num_pvsclusterbytes, loadmodel->brush.num_pvsclusters);
4764         totalchains = loadmodel->brush.num_pvsclusterbytes * loadmodel->brush.num_pvsclusters;
4765         if (l->filelen < totalchains + (int)sizeof(*in))
4766                 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);
4767
4768         loadmodel->brush.data_pvsclusters = Mem_Alloc(loadmodel->mempool, totalchains);
4769         memcpy(loadmodel->brush.data_pvsclusters, (qbyte *)(in + 1), totalchains);
4770 }
4771
4772 static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4773 {
4774         // FIXME: finish this code
4775         VectorCopy(in, out);
4776 }
4777
4778 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4779 {
4780         int i, j, k, index[3];
4781         float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4782         q3dlightgrid_t *a, *s;
4783         // FIXME: write this
4784         if (!model->brushq3.num_lightgrid)
4785         {
4786                 ambientcolor[0] = 1;
4787                 ambientcolor[1] = 1;
4788                 ambientcolor[2] = 1;
4789                 return;
4790         }
4791         Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4792         //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4793         //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4794         transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0] - 1);
4795         transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1] - 1);
4796         transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2] - 1);
4797         index[0] = (int)floor(transformed[0]);
4798         index[1] = (int)floor(transformed[1]);
4799         index[2] = (int)floor(transformed[2]);
4800         //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4801         // now lerp the values
4802         VectorClear(diffusenormal);
4803         a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4804         for (k = 0;k < 2;k++)
4805         {
4806                 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4807                 if (blend1 < 0.001f || index[2] + k >= model->brushq3.num_lightgrid_isize[2])
4808                         continue;
4809                 for (j = 0;j < 2;j++)
4810                 {
4811                         blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4812                         if (blend2 < 0.001f || index[1] + j >= model->brushq3.num_lightgrid_isize[1])
4813                                 continue;
4814                         for (i = 0;i < 2;i++)
4815                         {
4816                                 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4817                                 if (blend < 0.001f || index[0] + i >= model->brushq3.num_lightgrid_isize[0])
4818                                         continue;
4819                                 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4820                                 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4821                                 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4822                                 pitch = s->diffusepitch * M_PI / 128;
4823                                 yaw = s->diffuseyaw * M_PI / 128;
4824                                 sinpitch = sin(pitch);
4825                                 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4826                                 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4827                                 diffusenormal[2] += blend * (cos(pitch));
4828                                 //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)));
4829                         }
4830                 }
4831         }
4832         VectorNormalize(diffusenormal);
4833         //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]);
4834 }
4835
4836 static void Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const vec3_t point, int markframe)
4837 {
4838         int i;
4839         q3mleaf_t *leaf;
4840         colbrushf_t *brush;
4841         // find which leaf the point is in
4842         while (node->plane)
4843                 node = node->children[DotProduct(point, node->plane->normal) < node->plane->dist];
4844         // point trace the brushes
4845         leaf = (q3mleaf_t *)node;
4846         for (i = 0;i < leaf->numleafbrushes;i++)
4847         {
4848                 brush = leaf->firstleafbrush[i]->colbrushf;
4849                 if (brush && brush->markframe != markframe && BoxesOverlap(point, point, brush->mins, brush->maxs))
4850                 {
4851                         brush->markframe = markframe;
4852                         Collision_TracePointBrushFloat(trace, point, leaf->firstleafbrush[i]->colbrushf);
4853                 }
4854         }
4855         // can't do point traces on curves (they have no thickness)
4856 }
4857
4858 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)
4859 {
4860         int i, startside, endside;
4861         float dist1, dist2, midfrac, mid[3], nodesegmentmins[3], nodesegmentmaxs[3];
4862         q3mleaf_t *leaf;
4863         q3msurface_t *face;
4864         colbrushf_t *brush;
4865         if (startfrac > trace->realfraction)
4866                 return;
4867         // note: all line fragments past first impact fraction are ignored
4868         if (VectorCompare(start, end))
4869         {
4870                 // find which leaf the point is in
4871                 while (node->plane)
4872                         node = node->children[DotProduct(start, node->plane->normal) < node->plane->dist];
4873         }
4874         else
4875         {
4876                 // find which nodes the line is in and recurse for them
4877                 while (node->plane)
4878                 {
4879                         // recurse down node sides
4880                         dist1 = PlaneDiff(start, node->plane);
4881                         dist2 = PlaneDiff(end, node->plane);
4882                         startside = dist1 < 0;
4883                         endside = dist2 < 0;
4884                         if (startside == endside)
4885                         {
4886                                 // most of the time the line fragment is on one side of the plane
4887                                 node = node->children[startside];
4888                         }
4889                         else
4890                         {
4891                                 // line crosses node plane, split the line
4892                                 midfrac = dist1 / (dist1 - dist2);
4893                                 VectorLerp(start, midfrac, end, mid);
4894                                 // take the near side first
4895                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
4896                                 if (midfrac <= trace->realfraction)
4897                                         Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
4898                                 return;
4899                         }
4900                 }
4901         }
4902         // hit a leaf
4903         nodesegmentmins[0] = min(start[0], end[0]);
4904         nodesegmentmins[1] = min(start[1], end[1]);
4905         nodesegmentmins[2] = min(start[2], end[2]);
4906         nodesegmentmaxs[0] = max(start[0], end[0]);
4907         nodesegmentmaxs[1] = max(start[1], end[1]);
4908         nodesegmentmaxs[2] = max(start[2], end[2]);
4909         // line trace the brushes
4910         leaf = (q3mleaf_t *)node;
4911         for (i = 0;i < leaf->numleafbrushes;i++)
4912         {
4913                 brush = leaf->firstleafbrush[i]->colbrushf;
4914                 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
4915                 {
4916                         brush->markframe = markframe;
4917                         Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4918                         if (startfrac > trace->realfraction)
4919                                 return;
4920                 }
4921         }
4922         // can't do point traces on curves (they have no thickness)
4923         if (mod_q3bsp_curves_collisions.integer && !VectorCompare(start, end))
4924         {
4925                 // line trace the curves
4926                 for (i = 0;i < leaf->numleaffaces;i++)
4927                 {
4928                         face = leaf->firstleafface[i];
4929                         if (face->num_collisiontriangles && face->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
4930                         {
4931                                 face->collisionmarkframe = markframe;
4932                                 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4933                                 if (startfrac > trace->realfraction)
4934                                         return;
4935                         }
4936                 }
4937         }
4938 }
4939
4940 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)
4941 {
4942         int i;
4943         //int sides;
4944         float nodesegmentmins[3], nodesegmentmaxs[3];
4945         q3mleaf_t *leaf;
4946         colbrushf_t *brush;
4947         q3msurface_t *face;
4948         /*
4949                 // find which nodes the line is in and recurse for them
4950                 while (node->plane)
4951                 {
4952                         // recurse down node sides
4953                         int startside, endside;
4954                         float dist1near, dist1far, dist2near, dist2far;
4955                         BoxPlaneCornerDistances(thisbrush_start->mins, thisbrush_start->maxs, node->plane, &dist1near, &dist1far);
4956                         BoxPlaneCornerDistances(thisbrush_end->mins, thisbrush_end->maxs, node->plane, &dist2near, &dist2far);
4957                         startside = dist1near < 0;
4958                         startside = dist1near < 0 ? (dist1far < 0 ? 1 : 2) : (dist1far < 0 ? 2 : 0);
4959                         endside = dist2near < 0 ? (dist2far < 0 ? 1 : 2) : (dist2far < 0 ? 2 : 0);
4960                         if (startside == 2 || endside == 2)
4961                         {
4962                                 // brushes cross plane
4963                                 // do not clip anything, just take both sides
4964                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4965                                 node = node->children[1];
4966                                 continue;
4967                         }
4968                         if (startside == 0)
4969                         {
4970                                 if (endside == 0)
4971                                 {
4972                                         node = node->children[0];
4973                                         continue;
4974                                 }
4975                                 else
4976                                 {
4977                                         //midf0 = dist1near / (dist1near - dist2near);
4978                                         //midf1 = dist1far / (dist1far - dist2far);
4979                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4980                                         node = node->children[1];
4981                                         continue;
4982                                 }
4983                         }
4984                         else
4985                         {
4986                                 if (endside == 0)
4987                                 {
4988                                         //midf0 = dist1near / (dist1near - dist2near);
4989                                         //midf1 = dist1far / (dist1far - dist2far);
4990                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4991                                         node = node->children[1];
4992                                         continue;
4993                                 }
4994                                 else
4995                                 {
4996                                         node = node->children[1];
4997                                         continue;
4998                                 }
4999                         }
5000
5001                         if (dist1near <  0 && dist2near <  0 && dist1far <  0 && dist2far <  0){node = node->children[1];continue;}
5002                         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;}
5003                         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;}
5004                         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;}
5005                         if (dist1near <  0 && dist2near >= 0 && dist1far <  0 && dist2far <  0){node = node->children[1];continue;}
5006                         if (dist1near <  0 && dist2near >= 0 && dist1far <  0 && dist2far >= 0){}
5007                         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;}
5008                         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;}
5009                         if (dist1near >= 0 && dist2near <  0 && dist1far <  0 && dist2far <  0){node = node->children[1];continue;}
5010                         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;}
5011                         if (dist1near >= 0 && dist2near <  0 && dist1far >= 0 && dist2far <  0){}
5012                         if (dist1near >= 0 && dist2near <  0 && dist1far >= 0 && dist2far >= 0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
5013                         if (dist1near >= 0 && dist2near >= 0 && dist1far <  0 && dist2far <  0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
5014                         if (dist1near >= 0 && dist2near >= 0 && dist1far <  0 && dist2far >= 0){node = node->children[0];continue;}
5015                         if (dist1near >= 0 && dist2near >= 0 && dist1far >= 0 && dist2far <  0){node = node->children[0];continue;}
5016                         if (dist1near >= 0 && dist2near >= 0 && dist1far >= 0 && dist2far >= 0){node = node->children[0];continue;}
5017                         {             
5018                                 if (dist2near < 0) // d1n<0 && d2n<0
5019                                 {
5020                                         if (dist2near < 0) // d1n<0 && d2n<0
5021                                         {
5022                                                 if (dist2near < 0) // d1n<0 && d2n<0
5023                                                 {
5024                                                 }
5025                                                 else // d1n<0 && d2n>0
5026                                                 {
5027                                                 }
5028                                         }
5029                                         else // d1n<0 && d2n>0
5030                                         {
5031                                                 if (dist2near < 0) // d1n<0 && d2n<0
5032                                                 {
5033                                                 }
5034                                                 else // d1n<0 && d2n>0
5035                                                 {
5036                                                 }
5037                                         }
5038                                 }
5039                                 else // d1n<0 && d2n>0
5040                                 {
5041                                 }
5042                         }
5043                         else // d1n>0
5044                         {
5045                                 if (dist2near < 0) // d1n>0 && d2n<0
5046                                 {
5047                                 }
5048                                 else // d1n>0 && d2n>0
5049                                 {
5050                                 }
5051                         }
5052                         if (dist1near < 0 == dist1far < 0 == dist2near < 0 == dist2far < 0)
5053                         {
5054                                 node = node->children[startside];
5055                                 continue;
5056                         }
5057                         if (dist1near < dist2near)
5058                         {
5059                                 // out
5060                                 if (dist1near >= 0)
5061                                 {
5062                                         node = node->children[0];
5063                                         continue;
5064                                 }
5065                                 if (dist2far < 0)
5066                                 {
5067                                         node = node->children[1];
5068                                         continue;
5069                                 }
5070                                 // dist1near < 0 && dist2far >= 0
5071                         }
5072                         else
5073                         {
5074                                 // in
5075                         }
5076                         startside = dist1near < 0 ? (dist1far < 0 ? 1 : 2) : (dist1far < 0 ? 2 : 0);
5077                         endside = dist2near < 0 ? (dist2far < 0 ? 1 : 2) : (dist2far < 0 ? 2 : 0);
5078                         if (startside == 2 || endside == 2)
5079                         {
5080                                 // brushes cross plane
5081                                 // do not clip anything, just take both sides
5082                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5083                                 node = node->children[1];
5084                         }
5085                         else if (startside == endside)
5086                                 node = node->children[startside];
5087                         else if (startside == 0) // endside = 1 (start infront, end behind)
5088                         {
5089                         }
5090                         else // startside == 1 endside = 0 (start behind, end infront)
5091                         {
5092                         }
5093                         == endside)
5094                         {
5095                                 if (startside < 2)
5096                                         node = node->children[startside];
5097                                 else
5098                                 {
5099                                         // start and end brush cross plane
5100                                 }
5101                         }
5102                         else
5103                         {
5104                         }
5105                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5106                                 node = node->children[1];
5107                         else if (dist1near < 0 && dist1far < 0 && dist2near >= 0 && dist2far >= 0)
5108                         else if (dist1near >= 0 && dist1far >= 0 && dist2near < 0 && dist2far < 0)
5109                         else if (dist1near >= 0 && dist1far >= 0 && dist2near >= 0 && dist2far >= 0)
5110                                 node = node->children[0];
5111                         else
5112                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5113                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5114                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5115                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5116                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5117                         {
5118                         }
5119                         else if (dist1near >= 0 && dist1far >= 0)
5120                         {
5121                         }
5122                         else // mixed (lying on plane)
5123                         {
5124                         }
5125                         {
5126                                 if (dist2near < 0 && dist2far < 0)
5127                                 {
5128                                 }
5129                                 else
5130                                         node = node->children[1];
5131                         }
5132                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5133                                 node = node->children[0];
5134                         else if (dist1near >= 0 && dist1far >= 0 && dist2near >= 0 && dist2far >= 0)
5135                                 node = node->children[1];
5136                         else
5137                         {
5138                                 // both sides
5139                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
5140                                 node = node->children[1];
5141                         }
5142                         sides = dist1near || dist1near < 0 | dist1far < 0 | dist2near < 0 | dist
5143                         startside = dist1 < 0;
5144                         endside = dist2 < 0;
5145                         if (startside == endside)
5146                         {
5147                                 // most of the time the line fragment is on one side of the plane
5148                                 node = node->children[startside];
5149                         }
5150                         else
5151                         {
5152                                 // line crosses node plane, split the line
5153                                 midfrac = dist1 / (dist1 - dist2);
5154                                 VectorLerp(start, midfrac, end, mid);
5155                                 // take the near side first
5156                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
5157                                 if (midfrac <= trace->fraction)
5158                                         Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
5159                                 return;
5160                         }
5161                 }
5162         */
5163 #if 1
5164         for (;;)
5165         {
5166                 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5167                 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5168                 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5169                 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5170                 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5171                 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5172                 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
5173                         return;
5174                 if (!node->plane)
5175                         break;
5176                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5177                 node = node->children[1];
5178         }
5179 #elif 0
5180         // FIXME: could be made faster by copying TraceLine code and making it use
5181         // box plane distances...  (variant on the BoxOnPlaneSide code)
5182         for (;;)
5183         {
5184                 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5185                 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5186                 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5187                 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5188                 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5189                 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5190                 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
5191                         return;
5192                 if (!node->plane)
5193                         break;
5194                 if (mod_q3bsp_debugtracebrush.integer == 2)
5195                 {
5196                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5197                         node = node->children[1];
5198                         continue;
5199                 }
5200                 else if (mod_q3bsp_debugtracebrush.integer == 1)
5201                 {
5202                         // recurse down node sides
5203                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5204                         if (sides == 3)
5205                         {
5206                                 // segment box crosses plane
5207                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5208                                 node = node->children[1];
5209                                 continue;
5210                         }
5211                         // take whichever side the segment box is on
5212                         node = node->children[sides - 1];
5213                         continue;
5214                 }
5215                 else
5216                 {
5217                         // recurse down node sides
5218                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5219                         if (sides == 3)
5220                         {
5221                                 // segment box crosses plane
5222                                 // now check start and end brush boxes to handle a lot of 'diagonal' cases more efficiently...
5223                                 sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, node->plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, node->plane);
5224                                 if (sides == 3)
5225                                 {
5226                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5227                                         node = node->children[1];
5228                                         continue;
5229                                 }
5230                         }
5231                         // take whichever side the segment box is on
5232                         node = node->children[sides - 1];
5233                         continue;
5234                 }
5235                 return;
5236         }
5237 #else
5238         // FIXME: could be made faster by copying TraceLine code and making it use
5239         // box plane distances...  (variant on the BoxOnPlaneSide code)
5240         for (;;)
5241         {
5242                 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5243                 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5244                 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5245                 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5246                 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5247                 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5248                 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
5249                         return;
5250                 if (!node->plane)
5251                         break;
5252                 if (mod_q3bsp_debugtracebrush.integer == 2)
5253                 {
5254                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5255                         node = node->children[1];
5256                 }
5257                 else if (mod_q3bsp_debugtracebrush.integer == 1)
5258                 {
5259                         // recurse down node sides
5260                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5261                         if (sides == 3)
5262                         {
5263                                 // segment box crosses plane
5264                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5265                                 node = node->children[1];
5266                         }
5267                         else
5268                         {
5269                                 // take whichever side the segment box is on
5270                                 node = node->children[sides - 1];
5271                         }
5272                 }
5273                 else
5274                 {
5275                         // recurse down node sides
5276                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5277                         if (sides == 3)
5278                         {
5279                                 // segment box crosses plane
5280                                 // now check start and end brush boxes to handle a lot of 'diagonal' cases more efficiently...
5281                                 sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, node->plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, node->plane);
5282                                 if (sides == 3)
5283                                 {
5284                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5285                                         sides = 2;
5286                                 }
5287                         }
5288                         // take whichever side the segment box is on
5289                         node = node->children[sides - 1];
5290                 }
5291         }
5292 #endif
5293         // hit a leaf
5294         leaf = (q3mleaf_t *)node;
5295         for (i = 0;i < leaf->numleafbrushes;i++)
5296         {
5297                 brush = leaf->firstleafbrush[i]->colbrushf;
5298                 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
5299                 {
5300                         brush->markframe = markframe;
5301                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
5302                 }
5303         }
5304         if (mod_q3bsp_curves_collisions.integer)
5305         {
5306                 for (i = 0;i < leaf->numleaffaces;i++)
5307                 {
5308                         face = leaf->firstleafface[i];
5309                         if (face->num_collisiontriangles && face->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
5310                         {
5311                                 face->collisionmarkframe = markframe;
5312                                 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
5313                         }
5314                 }
5315         }
5316 }
5317
5318 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)
5319 {
5320         int i;
5321         float segmentmins[3], segmentmaxs[3];
5322         colbrushf_t *thisbrush_start, *thisbrush_end;
5323         matrix4x4_t startmatrix, endmatrix;
5324         static int markframe = 0;
5325         q3msurface_t *face;
5326         memset(trace, 0, sizeof(*trace));
5327         trace->fraction = 1;
5328         trace->realfraction = 1;
5329         trace->hitsupercontentsmask = hitsupercontentsmask;
5330         Matrix4x4_CreateIdentity(&startmatrix);
5331         Matrix4x4_CreateIdentity(&endmatrix);
5332         segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
5333         segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
5334         segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
5335         segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
5336         segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
5337         segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
5338         if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
5339         {
5340                 if (VectorCompare(boxstartmins, boxendmins))
5341                 {
5342                         // point trace
5343                         if (model->brushq3.submodel)
5344                         {
5345                                 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
5346                                         if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
5347                                                 Collision_TracePointBrushFloat(trace, boxstartmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
5348                         }
5349                         else
5350                                 Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, ++markframe);
5351                 }
5352                 else
5353                 {
5354                         // line trace
5355                         if (model->brushq3.submodel)
5356                         {
5357                                 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
5358                                         if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
5359                                                 Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
5360                                 if (mod_q3bsp_curves_collisions.integer)
5361                                 {
5362                                         for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
5363                                         {
5364                                                 face = model->brushq3.data_thismodel->firstface + i;
5365                                                 if (face->num_collisiontriangles)
5366                                                         Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
5367                                         }
5368                                 }
5369                         }
5370                         else
5371                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe, segmentmins, segmentmaxs);
5372                 }
5373         }
5374         else
5375         {
5376                 // box trace, performed as brush trace
5377                 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
5378                 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
5379                 if (model->brushq3.submodel)
5380                 {
5381                         for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
5382                                 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
5383                                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
5384                         if (mod_q3bsp_curves_collisions.integer)
5385                         {
5386                                 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
5387                                 {
5388                                         face = model->brushq3.data_thismodel->firstface + i;
5389                                         if (face->num_collisiontriangles)
5390                                                 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
5391                                 }
5392                         }
5393                 }
5394                 else
5395                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
5396         }
5397 }
5398
5399 static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
5400 {
5401         int clusterindex, side, nodestackindex = 0;
5402         q3mnode_t *node, *nodestack[1024];
5403         node = model->brushq3.data_nodes;
5404         if (!model->brush.num_pvsclusters)
5405                 return true;
5406         for (;;)
5407         {
5408                 if (node->plane)
5409                 {
5410                         // node - recurse down the BSP tree
5411                         side = BoxOnPlaneSide(mins, maxs, node->plane) - 1;
5412                         if (side < 2)
5413                         {
5414                                 // box is on one side of plane, take that path
5415                                 node = node->children[side];
5416                         }
5417                         else
5418                         {
5419                                 // box crosses plane, take one path and remember the other
5420                                 if (nodestackindex < 1024)
5421                                         nodestack[nodestackindex++] = node->children[0];
5422                                 node = node->children[1];
5423                         }
5424                 }
5425                 else
5426                 {
5427                         // leaf - check cluster bit
5428                         clusterindex = ((q3mleaf_t *)node)->clusterindex;
5429 #if 0
5430                         if (clusterindex >= model->brush.num_pvsclusters)
5431                         {
5432                                 Con_Printf("%i >= %i\n", clusterindex, model->brush.num_pvsclusters);
5433                                 return true;
5434                         }
5435 #endif
5436                         if (CHECKPVSBIT(pvs, clusterindex))
5437                         {
5438                                 // it is visible, return immediately with the news
5439                                 return true;
5440                         }
5441                         else
5442                         {
5443                                 // nothing to see here, try another path we didn't take earlier
5444                                 if (nodestackindex == 0)
5445                                         break;
5446                                 node = nodestack[--nodestackindex];
5447                         }
5448                 }
5449         }
5450         // it is not visible
5451         return false;
5452 }
5453
5454 //Returns PVS data for a given point
5455 //(note: can return NULL)
5456 static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
5457 {
5458         q3mnode_t *node;
5459         Mod_CheckLoaded(model);
5460         node = model->brushq3.data_nodes;
5461         while (node->plane)
5462                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
5463         if (((q3mleaf_t *)node)->clusterindex >= 0)
5464                 return model->brush.data_pvsclusters + ((q3mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
5465         else
5466                 return NULL;
5467 }
5468
5469 static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node)
5470 {
5471         while (node->plane)
5472         {
5473                 float d = PlaneDiff(org, node->plane);
5474                 if (d > radius)
5475                         node = node->children[0];
5476                 else if (d < -radius)
5477                         node = node->children[1];
5478                 else
5479                 {
5480                         // go down both sides
5481                         Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
5482                         node = node->children[1];
5483                 }
5484         }
5485         // if this leaf is in a cluster, accumulate the pvs bits
5486         if (((q3mleaf_t *)node)->clusterindex >= 0)
5487         {
5488                 int i;
5489                 qbyte *pvs = model->brush.data_pvsclusters + ((q3mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
5490                 for (i = 0;i < pvsbytes;i++)
5491                         pvsbuffer[i] |= pvs[i];
5492         }
5493 }
5494
5495 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
5496 //of the given point.
5497 static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
5498 {
5499         int bytes = model->brush.num_pvsclusterbytes;
5500         bytes = min(bytes, pvsbufferlength);
5501         if (r_novis.integer || !model->brush.num_pvsclusters || !Mod_Q3BSP_GetPVS(model, org))
5502         {
5503                 memset(pvsbuffer, 0xFF, bytes);
5504                 return bytes;
5505         }
5506         memset(pvsbuffer, 0, bytes);
5507         Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes);
5508         return bytes;
5509 }
5510
5511
5512 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
5513 {
5514         int supercontents = 0;
5515         if (nativecontents & Q2CONTENTS_SOLID)
5516                 supercontents |= SUPERCONTENTS_SOLID;
5517         if (nativecontents & Q2CONTENTS_WATER)
5518                 supercontents |= SUPERCONTENTS_WATER;
5519         if (nativecontents & Q2CONTENTS_SLIME)
5520                 supercontents |= SUPERCONTENTS_SLIME;
5521         if (nativecontents & Q2CONTENTS_LAVA)
5522                 supercontents |= SUPERCONTENTS_LAVA;
5523         return supercontents;
5524 }
5525
5526 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
5527 {
5528         int nativecontents = 0;
5529         if (supercontents & SUPERCONTENTS_SOLID)
5530                 nativecontents |= Q2CONTENTS_SOLID;
5531         if (supercontents & SUPERCONTENTS_WATER)
5532                 nativecontents |= Q2CONTENTS_WATER;
5533         if (supercontents & SUPERCONTENTS_SLIME)
5534                 nativecontents |= Q2CONTENTS_SLIME;
5535         if (supercontents & SUPERCONTENTS_LAVA)
5536                 nativecontents |= Q2CONTENTS_LAVA;
5537         return nativecontents;
5538 }
5539
5540 /*
5541 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)
5542 {
5543         mleaf_t *leaf;
5544         for (;;)
5545         {
5546                 if (!BoxesOverlap(node->mins, node->maxs, mins, maxs))
5547                         return;
5548                 if (!node->plane)
5549                         break;
5550                 Mod_Q3BSP_RecursiveGetVisible(node->children[0], model, point, mins, maxs, maxleafs, leaflist, numleafs, maxsurfaces, surfacelist, numsurfaces, pvs);
5551                 node = node->children[1];
5552         }
5553         leaf = (mleaf_t *)node;
5554         if ((pvs == NULL || CHECKPVSBIT(pvs, leaf->clusterindex)))
5555         {
5556                 int marksurfacenum;
5557                 q3msurface_t *surf;
5558                 if (maxleafs && *numleafs < maxleafs)
5559                         leaflist[(*numleaf)++] = leaf;
5560                 if (maxsurfaces)
5561                 {
5562                         for (marksurfacenum = 0;marksurfacenum < leaf->nummarksurfaces;marksurfacenum++)
5563                         {
5564                                 face = leaf->firstleafface[marksurfacenum];
5565                                 if (face->shadowmark != shadowmarkcount)
5566                                 {
5567                                         face->shadowmark = shadowmarkcount;
5568                                         if (BoxesOverlap(mins, maxs, face->mins, face->maxs) && *numsurfaces < maxsurfaces)
5569                                                 surfacelist[(*numsurfaces)++] = face;
5570                                 }
5571                         }
5572                 }
5573         }
5574 }
5575
5576 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)
5577 {
5578         // FIXME: support portals
5579         if (maxsurfaces)
5580                 *numsurfaces = 0;
5581         if (maxleafs)
5582                 *numleafs = 0;
5583         if (model->submodel)
5584         {
5585                 if (maxsurfaces)
5586                 {
5587                         for (marksurfacenum = 0;marksurfacenum < leaf->nummarksurfaces;marksurfacenum++)
5588                         {
5589                                 face = ent->model->brushq3.surfaces + leaf->firstmarksurface[marksurfacenum];
5590                                 if (BoxesOverlap(mins, maxs, face->mins, face->maxs) && *numsurfaces < maxsurfaces)
5591                                         surfacelist[(*numsurfaces)++] = face;
5592                         }
5593                 }
5594         }
5595         else
5596         {
5597                 pvs = ent->model->brush.GetPVS(ent->model, relativelightorigin);
5598                 Mod_Q3BSP_RecursiveGetVisible(ent->model->brushq3.data_nodes, model, point, mins, maxs, maxleafs, leaflist, numleafs, maxsurfaces, surfacelist, numsurfaces, pvs);
5599         }
5600 }
5601 */
5602
5603 void Mod_Q3BSP_BuildTextureFaceLists(void)
5604 {
5605         int i, j;
5606         loadmodel->brushq3.data_texturefaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummodelsurfaces * sizeof(q3msurface_t *));
5607         loadmodel->brushq3.data_texturefacenums = Mem_Alloc(loadmodel->mempool, loadmodel->nummodelsurfaces * sizeof(int));
5608         for (i = 0;i < loadmodel->brushq3.num_textures;i++)
5609                 loadmodel->brushq3.data_textures[i].numfaces = 0;
5610         for (i = 0;i < loadmodel->nummodelsurfaces;i++)
5611                 loadmodel->brushq3.data_faces[loadmodel->surfacelist[i]].texture->numfaces++;
5612         j = 0;
5613         for (i = 0;i < loadmodel->brushq3.num_textures;i++)
5614         {
5615                 loadmodel->brushq3.data_textures[i].facelist = loadmodel->brushq3.data_texturefaces + j;
5616                 loadmodel->brushq3.data_textures[i].facenumlist = loadmodel->brushq3.data_texturefacenums + j;
5617                 j += loadmodel->brushq3.data_textures[i].numfaces;
5618                 loadmodel->brushq3.data_textures[i].numfaces = 0;
5619         }
5620         for (i = 0;i < loadmodel->nummodelsurfaces;i++)
5621         {
5622                 loadmodel->brushq3.data_faces[loadmodel->surfacelist[i]].texture->facenumlist[loadmodel->brushq3.data_faces[loadmodel->surfacelist[i]].texture->numfaces] = loadmodel->surfacelist[i];
5623                 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];
5624         }
5625 }
5626
5627 void Mod_Q3BSP_RecursiveFindNumLeafs(q3mnode_t *node)
5628 {
5629         int numleafs;
5630         while (node->plane)
5631         {
5632                 Mod_Q3BSP_RecursiveFindNumLeafs(node->children[0]);
5633                 node = node->children[1];
5634         }
5635         numleafs = ((q3mleaf_t *)node - loadmodel->brushq3.data_leafs) + 1;
5636         if (loadmodel->brushq3.num_leafs < numleafs)
5637                 loadmodel->brushq3.num_leafs = numleafs;
5638 }
5639
5640 extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
5641 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
5642 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);
5643 extern void R_Q3BSP_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, int numsurfaces, const int *surfacelist);
5644 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);
5645 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
5646 {
5647         int i, j, numshadowmeshtriangles;
5648         q3dheader_t *header;
5649         float corner[3], yawradius, modelradius;
5650         q3msurface_t *face;
5651
5652         mod->type = mod_brushq3;
5653         mod->numframes = 1;
5654         mod->numskins = 1;
5655
5656         header = (q3dheader_t *)buffer;
5657
5658         i = LittleLong(header->version);
5659         if (i != Q3BSPVERSION)
5660                 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
5661         if (mod->isworldmodel)
5662         {
5663                 Cvar_SetValue("halflifebsp", false);
5664                 // until we get a texture for it...
5665                 R_ResetQuakeSky();
5666         }
5667
5668         mod->soundfromcenter = true;
5669         mod->TraceBox = Mod_Q3BSP_TraceBox;
5670         mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
5671         mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
5672         mod->brush.GetPVS = Mod_Q3BSP_GetPVS;
5673         mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
5674         mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
5675         mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
5676         mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
5677         //mod->DrawSky = R_Q3BSP_DrawSky;
5678         mod->Draw = R_Q3BSP_Draw;
5679         mod->GetLightInfo = R_Q3BSP_GetLightInfo;
5680         mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
5681         mod->DrawLight = R_Q3BSP_DrawLight;
5682
5683         mod_base = (qbyte *)header;
5684
5685         // swap all the lumps
5686         header->ident = LittleLong(header->ident);
5687         header->version = LittleLong(header->version);
5688         for (i = 0;i < Q3HEADER_LUMPS;i++)
5689         {
5690                 header->lumps[i].fileofs = LittleLong(header->lumps[i].fileofs);
5691                 header->lumps[i].filelen = LittleLong(header->lumps[i].filelen);
5692         }
5693
5694         Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
5695         Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
5696         Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
5697         Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
5698         Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
5699         Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
5700         Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
5701         Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
5702         Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
5703         Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
5704         Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
5705         Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
5706         Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
5707         Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
5708         Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
5709         Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
5710         Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
5711         loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
5712
5713         // make a single combined shadow mesh to allow optimized shadow volume creation
5714         numshadowmeshtriangles = 0;
5715         for (j = 0, face = loadmodel->brushq3.data_faces;j < loadmodel->brushq3.num_faces;j++, face++)
5716         {
5717                 face->num_firstshadowmeshtriangle = numshadowmeshtriangles;
5718                 numshadowmeshtriangles += face->num_triangles;
5719         }
5720         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
5721         for (j = 0, face = loadmodel->brushq3.data_faces;j < loadmodel->brushq3.num_faces;j++, face++)
5722                 Mod_ShadowMesh_AddMesh(loadmodel->mempool, loadmodel->brush.shadowmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
5723         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true);
5724         Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
5725
5726         loadmodel->brushq3.num_leafs = 0;
5727         Mod_Q3BSP_RecursiveFindNumLeafs(loadmodel->brushq3.data_nodes);
5728
5729         mod = loadmodel;
5730         for (i = 0;i < loadmodel->brushq3.num_models;i++)
5731         {
5732                 if (i > 0)
5733                 {
5734                         char name[10];
5735                         // LordHavoc: only register submodels if it is the world
5736                         // (prevents external bsp models from replacing world submodels with
5737                         //  their own)
5738                         if (!loadmodel->isworldmodel)
5739                                 continue;
5740                         // duplicate the basic information
5741                         sprintf(name, "*%i", i);
5742                         mod = Mod_FindName(name);
5743                         *mod = *loadmodel;
5744                         strcpy(mod->name, name);
5745                         // textures and memory belong to the main model
5746                         mod->texturepool = NULL;
5747                         mod->mempool = NULL;
5748                         mod->brush.GetPVS = NULL;
5749                         mod->brush.FatPVS = NULL;
5750                         mod->brush.BoxTouchingPVS = NULL;
5751                         mod->brush.LightPoint = NULL;
5752                         mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
5753                 }
5754                 mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i;
5755                 mod->brushq3.submodel = i;
5756
5757                 // make the model surface list (used by shadowing/lighting)
5758                 mod->nummodelsurfaces = mod->brushq3.data_thismodel->numfaces;
5759                 mod->surfacelist = Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->surfacelist));
5760                 for (j = 0;j < mod->nummodelsurfaces;j++)
5761                         mod->surfacelist[j] = (mod->brushq3.data_thismodel->firstface - mod->brushq3.data_faces) + j;
5762
5763                 VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins);
5764                 VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs);
5765                 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
5766                 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
5767                 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
5768                 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
5769                 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
5770                 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
5771                 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
5772                 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
5773                 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
5774                 mod->yawmins[2] = mod->normalmins[2];
5775                 mod->yawmaxs[2] = mod->normalmaxs[2];
5776                 mod->radius = modelradius;
5777                 mod->radius2 = modelradius * modelradius;
5778
5779                 for (j = 0;j < mod->brushq3.data_thismodel->numfaces;j++)
5780                         if (mod->brushq3.data_thismodel->firstface[j].texture->surfaceflags & Q3SURFACEFLAG_SKY)
5781                                 break;
5782                 if (j < mod->brushq3.data_thismodel->numfaces)
5783                         mod->DrawSky = R_Q3BSP_DrawSky;
5784         }
5785
5786         Mod_Q3BSP_BuildTextureFaceLists();
5787 }
5788
5789 void Mod_IBSP_Load(model_t *mod, void *buffer)
5790 {
5791         int i = LittleLong(((int *)buffer)[1]);
5792         if (i == Q3BSPVERSION)
5793                 Mod_Q3BSP_Load(mod,buffer);
5794         else if (i == Q2BSPVERSION)
5795                 Mod_Q2BSP_Load(mod,buffer);
5796         else
5797                 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
5798 }
5799
5800 void Mod_MAP_Load(model_t *mod, void *buffer)
5801 {
5802         Host_Error("Mod_MAP_Load: not yet implemented\n");
5803 }
5804