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