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