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