hush some texture loading warnings in dedicated servers (thanks to Biomass for report...
[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);