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