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