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