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