]> icculus.org git repositories - divverent/darkplaces.git/blob - model_brush.c
implemented and debugged BIH (Bounding Interval Hierarchy) code, more
[divverent/darkplaces.git] / model_brush.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22 #include "image.h"
23 #include "r_shadow.h"
24 #include "polygon.h"
25 #include "curves.h"
26 #include "wad.h"
27
28
29 //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 r_novis = {0, "r_novis", "0", "draws whole level, see also sv_cullentities_pvs 0"};
31 cvar_t r_picmipworld = {CVAR_SAVE, "r_picmipworld", "1", "whether gl_picmip shall apply to world textures too"};
32 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)"};
33 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)"};
34 cvar_t r_subdivisions_mintess = {0, "r_subdivisions_mintess", "0", "minimum number of subdivisions (values above 0 will smooth curves that don't need it)"};
35 cvar_t r_subdivisions_maxtess = {0, "r_subdivisions_maxtess", "1024", "maximum number of subdivisions (prevents curves beyond a certain detail level, limits smoothing)"};
36 cvar_t r_subdivisions_maxvertices = {0, "r_subdivisions_maxvertices", "65536", "maximum vertices allowed per subdivided curve"};
37 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)"};
38 cvar_t r_subdivisions_collision_mintess = {0, "r_subdivisions_collision_mintess", "0", "minimum number of subdivisions (values above 0 will smooth curves that don't need it)"};
39 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)"};
40 cvar_t r_subdivisions_collision_maxvertices = {0, "r_subdivisions_collision_maxvertices", "4225", "maximum vertices allowed per subdivided curve"};
41 cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1", "enables collisions with curves (SLOW)"};
42 cvar_t mod_q3bsp_curves_collisions_stride = {0, "mod_q3bsp_curves_collisions_stride", "16", "collisions against curves: optimize performance by doing a combined collision check for this triangle amount first (-1 avoids any box tests)"};
43 cvar_t mod_q3bsp_curves_stride = {0, "mod_q3bsp_curves_stride", "16", "particle effect collisions against curves: optimize performance by doing a combined collision check for this triangle amount first (-1 avoids any box tests)"};
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 cvar_t mod_q3bsp_lightmapmergepower = {CVAR_SAVE, "mod_q3bsp_lightmapmergepower", "4", "merges the quake3 128x128 lightmap textures into larger lightmap group textures to speed up rendering, 1 = 256x256, 2 = 512x512, 3 = 1024x1024, 4 = 2048x2048, 5 = 4096x4096, ..."};
47 cvar_t mod_q3bsp_nolightmaps = {CVAR_SAVE, "mod_q3bsp_nolightmaps", "0", "do not load lightmaps in Q3BSP maps (to save video RAM, but be warned: it looks ugly)"};
48 cvar_t mod_q3bsp_tracelineofsight_brushes = {0, "mod_q3bsp_tracelineofsight_brushes", "0", "enables culling of entities behind detail brushes, curves, etc"};
49 cvar_t mod_collision_bih = {0, "mod_collision_bih", "0", "enables use of generated Bounding Interval Hierarchy tree instead of compiled bsp tree in collision code"};
50
51 static texture_t mod_q1bsp_texture_solid;
52 static texture_t mod_q1bsp_texture_sky;
53 static texture_t mod_q1bsp_texture_lava;
54 static texture_t mod_q1bsp_texture_slime;
55 static texture_t mod_q1bsp_texture_water;
56
57 void Mod_BrushInit(void)
58 {
59 //      Cvar_RegisterVariable(&r_subdivide_size);
60         Cvar_RegisterVariable(&r_novis);
61         Cvar_RegisterVariable(&r_picmipworld);
62         Cvar_RegisterVariable(&r_nosurftextures);
63         Cvar_RegisterVariable(&r_subdivisions_tolerance);
64         Cvar_RegisterVariable(&r_subdivisions_mintess);
65         Cvar_RegisterVariable(&r_subdivisions_maxtess);
66         Cvar_RegisterVariable(&r_subdivisions_maxvertices);
67         Cvar_RegisterVariable(&r_subdivisions_collision_tolerance);
68         Cvar_RegisterVariable(&r_subdivisions_collision_mintess);
69         Cvar_RegisterVariable(&r_subdivisions_collision_maxtess);
70         Cvar_RegisterVariable(&r_subdivisions_collision_maxvertices);
71         Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
72         Cvar_RegisterVariable(&mod_q3bsp_curves_collisions_stride);
73         Cvar_RegisterVariable(&mod_q3bsp_curves_stride);
74         Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
75         Cvar_RegisterVariable(&mod_q3bsp_debugtracebrush);
76         Cvar_RegisterVariable(&mod_q3bsp_lightmapmergepower);
77         Cvar_RegisterVariable(&mod_q3bsp_nolightmaps);
78         Cvar_RegisterVariable(&mod_q3bsp_tracelineofsight_brushes);
79         Cvar_RegisterVariable(&mod_collision_bih);
80
81         memset(&mod_q1bsp_texture_solid, 0, sizeof(mod_q1bsp_texture_solid));
82         strlcpy(mod_q1bsp_texture_solid.name, "solid" , sizeof(mod_q1bsp_texture_solid.name));
83         mod_q1bsp_texture_solid.surfaceflags = 0;
84         mod_q1bsp_texture_solid.supercontents = SUPERCONTENTS_SOLID;
85
86         mod_q1bsp_texture_sky = mod_q1bsp_texture_solid;
87         strlcpy(mod_q1bsp_texture_sky.name, "sky", sizeof(mod_q1bsp_texture_sky.name));
88         mod_q1bsp_texture_sky.surfaceflags = Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT | Q3SURFACEFLAG_NOMARKS | Q3SURFACEFLAG_NODLIGHT | Q3SURFACEFLAG_NOLIGHTMAP;
89         mod_q1bsp_texture_sky.supercontents = SUPERCONTENTS_SKY | SUPERCONTENTS_NODROP;
90
91         mod_q1bsp_texture_lava = mod_q1bsp_texture_solid;
92         strlcpy(mod_q1bsp_texture_lava.name, "*lava", sizeof(mod_q1bsp_texture_lava.name));
93         mod_q1bsp_texture_lava.surfaceflags = Q3SURFACEFLAG_NOMARKS;
94         mod_q1bsp_texture_lava.supercontents = SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP;
95
96         mod_q1bsp_texture_slime = mod_q1bsp_texture_solid;
97         strlcpy(mod_q1bsp_texture_slime.name, "*slime", sizeof(mod_q1bsp_texture_slime.name));
98         mod_q1bsp_texture_slime.surfaceflags = Q3SURFACEFLAG_NOMARKS;
99         mod_q1bsp_texture_slime.supercontents = SUPERCONTENTS_SLIME;
100
101         mod_q1bsp_texture_water = mod_q1bsp_texture_solid;
102         strlcpy(mod_q1bsp_texture_water.name, "*water", sizeof(mod_q1bsp_texture_water.name));
103         mod_q1bsp_texture_water.surfaceflags = Q3SURFACEFLAG_NOMARKS;
104         mod_q1bsp_texture_water.supercontents = SUPERCONTENTS_WATER;
105 }
106
107 static mleaf_t *Mod_Q1BSP_PointInLeaf(dp_model_t *model, const vec3_t p)
108 {
109         mnode_t *node;
110
111         if (model == NULL)
112                 return NULL;
113
114         // LordHavoc: modified to start at first clip node,
115         // in other words: first node of the (sub)model
116         node = model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode;
117         while (node->plane)
118                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
119
120         return (mleaf_t *)node;
121 }
122
123 static void Mod_Q1BSP_AmbientSoundLevelsForPoint(dp_model_t *model, const vec3_t p, unsigned char *out, int outsize)
124 {
125         int i;
126         mleaf_t *leaf;
127         leaf = Mod_Q1BSP_PointInLeaf(model, p);
128         if (leaf)
129         {
130                 i = min(outsize, (int)sizeof(leaf->ambient_sound_level));
131                 if (i)
132                 {
133                         memcpy(out, leaf->ambient_sound_level, i);
134                         out += i;
135                         outsize -= i;
136                 }
137         }
138         if (outsize)
139                 memset(out, 0, outsize);
140 }
141
142 static int Mod_Q1BSP_FindBoxClusters(dp_model_t *model, const vec3_t mins, const vec3_t maxs, int maxclusters, int *clusterlist)
143 {
144         int numclusters = 0;
145         int nodestackindex = 0;
146         mnode_t *node, *nodestack[1024];
147         if (!model->brush.num_pvsclusters)
148                 return -1;
149         node = model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode;
150         for (;;)
151         {
152 #if 1
153                 if (node->plane)
154                 {
155                         // node - recurse down the BSP tree
156                         int sides = BoxOnPlaneSide(mins, maxs, node->plane);
157                         if (sides < 3)
158                         {
159                                 if (sides == 0)
160                                         return -1; // ERROR: NAN bounding box!
161                                 // box is on one side of plane, take that path
162                                 node = node->children[sides-1];
163                         }
164                         else
165                         {
166                                 // box crosses plane, take one path and remember the other
167                                 if (nodestackindex < 1024)
168                                         nodestack[nodestackindex++] = node->children[0];
169                                 node = node->children[1];
170                         }
171                         continue;
172                 }
173                 else
174                 {
175                         // leaf - add clusterindex to list
176                         if (numclusters < maxclusters)
177                                 clusterlist[numclusters] = ((mleaf_t *)node)->clusterindex;
178                         numclusters++;
179                 }
180 #else
181                 if (BoxesOverlap(mins, maxs, node->mins, node->maxs))
182                 {
183                         if (node->plane)
184                         {
185                                 if (nodestackindex < 1024)
186                                         nodestack[nodestackindex++] = node->children[0];
187                                 node = node->children[1];
188                                 continue;
189                         }
190                         else
191                         {
192                                 // leaf - add clusterindex to list
193                                 if (numclusters < maxclusters)
194                                         clusterlist[numclusters] = ((mleaf_t *)node)->clusterindex;
195                                 numclusters++;
196                         }
197                 }
198 #endif
199                 // try another path we didn't take earlier
200                 if (nodestackindex == 0)
201                         break;
202                 node = nodestack[--nodestackindex];
203         }
204         // return number of clusters found (even if more than the maxclusters)
205         return numclusters;
206 }
207
208 static int Mod_Q1BSP_BoxTouchingPVS(dp_model_t *model, const unsigned char *pvs, const vec3_t mins, const vec3_t maxs)
209 {
210         int nodestackindex = 0;
211         mnode_t *node, *nodestack[1024];
212         if (!model->brush.num_pvsclusters)
213                 return true;
214         node = model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode;
215         for (;;)
216         {
217 #if 1
218                 if (node->plane)
219                 {
220                         // node - recurse down the BSP tree
221                         int sides = BoxOnPlaneSide(mins, maxs, node->plane);
222                         if (sides < 3)
223                         {
224                                 if (sides == 0)
225                                         return -1; // ERROR: NAN bounding box!
226                                 // box is on one side of plane, take that path
227                                 node = node->children[sides-1];
228                         }
229                         else
230                         {
231                                 // box crosses plane, take one path and remember the other
232                                 if (nodestackindex < 1024)
233                                         nodestack[nodestackindex++] = node->children[0];
234                                 node = node->children[1];
235                         }
236                         continue;
237                 }
238                 else
239                 {
240                         // leaf - check cluster bit
241                         int clusterindex = ((mleaf_t *)node)->clusterindex;
242                         if (CHECKPVSBIT(pvs, clusterindex))
243                         {
244                                 // it is visible, return immediately with the news
245                                 return true;
246                         }
247                 }
248 #else
249                 if (BoxesOverlap(mins, maxs, node->mins, node->maxs))
250                 {
251                         if (node->plane)
252                         {
253                                 if (nodestackindex < 1024)
254                                         nodestack[nodestackindex++] = node->children[0];
255                                 node = node->children[1];
256                                 continue;
257                         }
258                         else
259                         {
260                                 // leaf - check cluster bit
261                                 int clusterindex = ((mleaf_t *)node)->clusterindex;
262                                 if (CHECKPVSBIT(pvs, clusterindex))
263                                 {
264                                         // it is visible, return immediately with the news
265                                         return true;
266                                 }
267                         }
268                 }
269 #endif
270                 // nothing to see here, try another path we didn't take earlier
271                 if (nodestackindex == 0)
272                         break;
273                 node = nodestack[--nodestackindex];
274         }
275         // it is not visible
276         return false;
277 }
278
279 static int Mod_Q1BSP_BoxTouchingLeafPVS(dp_model_t *model, const unsigned char *pvs, const vec3_t mins, const vec3_t maxs)
280 {
281         int nodestackindex = 0;
282         mnode_t *node, *nodestack[1024];
283         if (!model->brush.num_leafs)
284                 return true;
285         node = model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode;
286         for (;;)
287         {
288 #if 1
289                 if (node->plane)
290                 {
291                         // node - recurse down the BSP tree
292                         int sides = BoxOnPlaneSide(mins, maxs, node->plane);
293                         if (sides < 3)
294                         {
295                                 if (sides == 0)
296                                         return -1; // ERROR: NAN bounding box!
297                                 // box is on one side of plane, take that path
298                                 node = node->children[sides-1];
299                         }
300                         else
301                         {
302                                 // box crosses plane, take one path and remember the other
303                                 if (nodestackindex < 1024)
304                                         nodestack[nodestackindex++] = node->children[0];
305                                 node = node->children[1];
306                         }
307                         continue;
308                 }
309                 else
310                 {
311                         // leaf - check cluster bit
312                         int clusterindex = ((mleaf_t *)node) - model->brush.data_leafs;
313                         if (CHECKPVSBIT(pvs, clusterindex))
314                         {
315                                 // it is visible, return immediately with the news
316                                 return true;
317                         }
318                 }
319 #else
320                 if (BoxesOverlap(mins, maxs, node->mins, node->maxs))
321                 {
322                         if (node->plane)
323                         {
324                                 if (nodestackindex < 1024)
325                                         nodestack[nodestackindex++] = node->children[0];
326                                 node = node->children[1];
327                                 continue;
328                         }
329                         else
330                         {
331                                 // leaf - check cluster bit
332                                 int clusterindex = ((mleaf_t *)node) - model->brush.data_leafs;
333                                 if (CHECKPVSBIT(pvs, clusterindex))
334                                 {
335                                         // it is visible, return immediately with the news
336                                         return true;
337                                 }
338                         }
339                 }
340 #endif
341                 // nothing to see here, try another path we didn't take earlier
342                 if (nodestackindex == 0)
343                         break;
344                 node = nodestack[--nodestackindex];
345         }
346         // it is not visible
347         return false;
348 }
349
350 static int Mod_Q1BSP_BoxTouchingVisibleLeafs(dp_model_t *model, const unsigned char *visibleleafs, const vec3_t mins, const vec3_t maxs)
351 {
352         int nodestackindex = 0;
353         mnode_t *node, *nodestack[1024];
354         if (!model->brush.num_leafs)
355                 return true;
356         node = model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode;
357         for (;;)
358         {
359 #if 1
360                 if (node->plane)
361                 {
362                         // node - recurse down the BSP tree
363                         int sides = BoxOnPlaneSide(mins, maxs, node->plane);
364                         if (sides < 3)
365                         {
366                                 if (sides == 0)
367                                         return -1; // ERROR: NAN bounding box!
368                                 // box is on one side of plane, take that path
369                                 node = node->children[sides-1];
370                         }
371                         else
372                         {
373                                 // box crosses plane, take one path and remember the other
374                                 if (nodestackindex < 1024)
375                                         nodestack[nodestackindex++] = node->children[0];
376                                 node = node->children[1];
377                         }
378                         continue;
379                 }
380                 else
381                 {
382                         // leaf - check if it is visible
383                         if (visibleleafs[(mleaf_t *)node - model->brush.data_leafs])
384                         {
385                                 // it is visible, return immediately with the news
386                                 return true;
387                         }
388                 }
389 #else
390                 if (BoxesOverlap(mins, maxs, node->mins, node->maxs))
391                 {
392                         if (node->plane)
393                         {
394                                 if (nodestackindex < 1024)
395                                         nodestack[nodestackindex++] = node->children[0];
396                                 node = node->children[1];
397                                 continue;
398                         }
399                         else
400                         {
401                                 // leaf - check if it is visible
402                                 if (visibleleafs[(mleaf_t *)node - model->brush.data_leafs])
403                                 {
404                                         // it is visible, return immediately with the news
405                                         return true;
406                                 }
407                         }
408                 }
409 #endif
410                 // nothing to see here, try another path we didn't take earlier
411                 if (nodestackindex == 0)
412                         break;
413                 node = nodestack[--nodestackindex];
414         }
415         // it is not visible
416         return false;
417 }
418
419 typedef struct findnonsolidlocationinfo_s
420 {
421         vec3_t center;
422         vec3_t absmin, absmax;
423         vec_t radius;
424         vec3_t nudge;
425         vec_t bestdist;
426         dp_model_t *model;
427 }
428 findnonsolidlocationinfo_t;
429
430 static void Mod_Q1BSP_FindNonSolidLocation_r_Triangle(findnonsolidlocationinfo_t *info, msurface_t *surface, int k)
431 {
432         int i, *tri;
433         float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
434
435         tri = (info->model->surfmesh.data_element3i + 3 * surface->num_firsttriangle) + k * 3;
436         VectorCopy((info->model->surfmesh.data_vertex3f + tri[0] * 3), vert[0]);
437         VectorCopy((info->model->surfmesh.data_vertex3f + tri[1] * 3), vert[1]);
438         VectorCopy((info->model->surfmesh.data_vertex3f + tri[2] * 3), vert[2]);
439         VectorSubtract(vert[1], vert[0], edge[0]);
440         VectorSubtract(vert[2], vert[1], edge[1]);
441         CrossProduct(edge[1], edge[0], facenormal);
442         if (facenormal[0] || facenormal[1] || facenormal[2])
443         {
444                 VectorNormalize(facenormal);
445                 f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
446                 if (f <= info->bestdist && f >= -info->bestdist)
447                 {
448                         VectorSubtract(vert[0], vert[2], edge[2]);
449                         VectorNormalize(edge[0]);
450                         VectorNormalize(edge[1]);
451                         VectorNormalize(edge[2]);
452                         CrossProduct(facenormal, edge[0], edgenormal[0]);
453                         CrossProduct(facenormal, edge[1], edgenormal[1]);
454                         CrossProduct(facenormal, edge[2], edgenormal[2]);
455                         // face distance
456                         if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
457                                         && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
458                                         && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
459                         {
460                                 // we got lucky, the center is within the face
461                                 dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
462                                 if (dist < 0)
463                                 {
464                                         dist = -dist;
465                                         if (info->bestdist > dist)
466                                         {
467                                                 info->bestdist = dist;
468                                                 VectorScale(facenormal, (info->radius - -dist), info->nudge);
469                                         }
470                                 }
471                                 else
472                                 {
473                                         if (info->bestdist > dist)
474                                         {
475                                                 info->bestdist = dist;
476                                                 VectorScale(facenormal, (info->radius - dist), info->nudge);
477                                         }
478                                 }
479                         }
480                         else
481                         {
482                                 // check which edge or vertex the center is nearest
483                                 for (i = 0;i < 3;i++)
484                                 {
485                                         f = DotProduct(info->center, edge[i]);
486                                         if (f >= DotProduct(vert[0], edge[i])
487                                                         && f <= DotProduct(vert[1], edge[i]))
488                                         {
489                                                 // on edge
490                                                 VectorMA(info->center, -f, edge[i], point);
491                                                 dist = sqrt(DotProduct(point, point));
492                                                 if (info->bestdist > dist)
493                                                 {
494                                                         info->bestdist = dist;
495                                                         VectorScale(point, (info->radius / dist), info->nudge);
496                                                 }
497                                                 // skip both vertex checks
498                                                 // (both are further away than this edge)
499                                                 i++;
500                                         }
501                                         else
502                                         {
503                                                 // not on edge, check first vertex of edge
504                                                 VectorSubtract(info->center, vert[i], point);
505                                                 dist = sqrt(DotProduct(point, point));
506                                                 if (info->bestdist > dist)
507                                                 {
508                                                         info->bestdist = dist;
509                                                         VectorScale(point, (info->radius / dist), info->nudge);
510                                                 }
511                                         }
512                                 }
513                         }
514                 }
515         }
516 }
517
518 static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
519 {
520         int surfacenum, k, *mark;
521         msurface_t *surface;
522         for (surfacenum = 0, mark = leaf->firstleafsurface;surfacenum < leaf->numleafsurfaces;surfacenum++, mark++)
523         {
524                 surface = info->model->data_surfaces + *mark;
525                 if (surface->texture->supercontents & SUPERCONTENTS_SOLID)
526                 {
527                         if(surface->deprecatedq3num_bboxstride > 0)
528                         {
529                                 int i, cnt, tri;
530                                 cnt = (surface->num_triangles + surface->deprecatedq3num_bboxstride - 1) / surface->deprecatedq3num_bboxstride;
531                                 for(i = 0; i < cnt; ++i)
532                                 {
533                                         if(BoxesOverlap(surface->deprecatedq3data_bbox6f + i * 6, surface->deprecatedq3data_bbox6f + i * 6 + 3, info->absmin, info->absmax))
534                                         {
535                                                 for(k = 0; k < surface->deprecatedq3num_bboxstride; ++k)
536                                                 {
537                                                         tri = i * surface->deprecatedq3num_bboxstride + k;
538                                                         if(tri >= surface->num_triangles)
539                                                                 break;
540                                                         Mod_Q1BSP_FindNonSolidLocation_r_Triangle(info, surface, tri);
541                                                 }
542                                         }
543                                 }
544                         }
545                         else
546                         {
547                                 for (k = 0;k < surface->num_triangles;k++)
548                                 {
549                                         Mod_Q1BSP_FindNonSolidLocation_r_Triangle(info, surface, k);
550                                 }
551                         }
552                 }
553         }
554 }
555
556 static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
557 {
558         if (node->plane)
559         {
560                 float f = PlaneDiff(info->center, node->plane);
561                 if (f >= -info->bestdist)
562                         Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[0]);
563                 if (f <= info->bestdist)
564                         Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[1]);
565         }
566         else
567         {
568                 if (((mleaf_t *)node)->numleafsurfaces)
569                         Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
570         }
571 }
572
573 static void Mod_Q1BSP_FindNonSolidLocation(dp_model_t *model, const vec3_t in, vec3_t out, float radius)
574 {
575         int i;
576         findnonsolidlocationinfo_t info;
577         if (model == NULL)
578         {
579                 VectorCopy(in, out);
580                 return;
581         }
582         VectorCopy(in, info.center);
583         info.radius = radius;
584         info.model = model;
585         i = 0;
586         do
587         {
588                 VectorClear(info.nudge);
589                 info.bestdist = radius;
590                 VectorCopy(info.center, info.absmin);
591                 VectorCopy(info.center, info.absmax);
592                 info.absmin[0] -= info.radius + 1;
593                 info.absmin[1] -= info.radius + 1;
594                 info.absmin[2] -= info.radius + 1;
595                 info.absmax[0] += info.radius + 1;
596                 info.absmax[1] += info.radius + 1;
597                 info.absmax[2] += info.radius + 1;
598                 Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode);
599                 VectorAdd(info.center, info.nudge, info.center);
600         }
601         while (info.bestdist < radius && ++i < 10);
602         VectorCopy(info.center, out);
603 }
604
605 int Mod_Q1BSP_SuperContentsFromNativeContents(dp_model_t *model, int nativecontents)
606 {
607         switch(nativecontents)
608         {
609                 case CONTENTS_EMPTY:
610                         return 0;
611                 case CONTENTS_SOLID:
612                         return SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
613                 case CONTENTS_WATER:
614                         return SUPERCONTENTS_WATER;
615                 case CONTENTS_SLIME:
616                         return SUPERCONTENTS_SLIME;
617                 case CONTENTS_LAVA:
618                         return SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP;
619                 case CONTENTS_SKY:
620                         return SUPERCONTENTS_SKY | SUPERCONTENTS_NODROP | SUPERCONTENTS_OPAQUE; // to match behaviour of Q3 maps, let sky count as opaque
621         }
622         return 0;
623 }
624
625 int Mod_Q1BSP_NativeContentsFromSuperContents(dp_model_t *model, int supercontents)
626 {
627         if (supercontents & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY))
628                 return CONTENTS_SOLID;
629         if (supercontents & SUPERCONTENTS_SKY)
630                 return CONTENTS_SKY;
631         if (supercontents & SUPERCONTENTS_LAVA)
632                 return CONTENTS_LAVA;
633         if (supercontents & SUPERCONTENTS_SLIME)
634                 return CONTENTS_SLIME;
635         if (supercontents & SUPERCONTENTS_WATER)
636                 return CONTENTS_WATER;
637         return CONTENTS_EMPTY;
638 }
639
640 typedef struct RecursiveHullCheckTraceInfo_s
641 {
642         // the hull we're tracing through
643         const hull_t *hull;
644
645         // the trace structure to fill in
646         trace_t *trace;
647
648         // start, end, and end - start (in model space)
649         double start[3];
650         double end[3];
651         double dist[3];
652 }
653 RecursiveHullCheckTraceInfo_t;
654
655 // 1/32 epsilon to keep floating point happy
656 #define DIST_EPSILON (0.03125)
657
658 #define HULLCHECKSTATE_EMPTY 0
659 #define HULLCHECKSTATE_SOLID 1
660 #define HULLCHECKSTATE_DONE 2
661
662 extern cvar_t collision_prefernudgedfraction;
663 static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int num, double p1f, double p2f, double p1[3], double p2[3])
664 {
665         // status variables, these don't need to be saved on the stack when
666         // recursing...  but are because this should be thread-safe
667         // (note: tracing against a bbox is not thread-safe, yet)
668         int ret;
669         mplane_t *plane;
670         double t1, t2;
671
672         // variables that need to be stored on the stack when recursing
673         mclipnode_t *node;
674         int side;
675         double midf, mid[3];
676
677         // LordHavoc: a goto!  everyone flee in terror... :)
678 loc0:
679         // check for empty
680         if (num < 0)
681         {
682                 num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
683                 if (!t->trace->startfound)
684                 {
685                         t->trace->startfound = true;
686                         t->trace->startsupercontents |= num;
687                 }
688                 if (num & SUPERCONTENTS_LIQUIDSMASK)
689                         t->trace->inwater = true;
690                 if (num == 0)
691                         t->trace->inopen = true;
692                 if (num & SUPERCONTENTS_SOLID)
693                         t->trace->hittexture = &mod_q1bsp_texture_solid;
694                 else if (num & SUPERCONTENTS_SKY)
695                         t->trace->hittexture = &mod_q1bsp_texture_sky;
696                 else if (num & SUPERCONTENTS_LAVA)
697                         t->trace->hittexture = &mod_q1bsp_texture_lava;
698                 else if (num & SUPERCONTENTS_SLIME)
699                         t->trace->hittexture = &mod_q1bsp_texture_slime;
700                 else
701                         t->trace->hittexture = &mod_q1bsp_texture_water;
702                 t->trace->hitq3surfaceflags = t->trace->hittexture->surfaceflags;
703                 t->trace->hitsupercontents = num;
704                 if (num & t->trace->hitsupercontentsmask)
705                 {
706                         // if the first leaf is solid, set startsolid
707                         if (t->trace->allsolid)
708                                 t->trace->startsolid = true;
709 #if COLLISIONPARANOID >= 3
710                         Con_Print("S");
711 #endif
712                         return HULLCHECKSTATE_SOLID;
713                 }
714                 else
715                 {
716                         t->trace->allsolid = false;
717 #if COLLISIONPARANOID >= 3
718                         Con_Print("E");
719 #endif
720                         return HULLCHECKSTATE_EMPTY;
721                 }
722         }
723
724         // find the point distances
725         node = t->hull->clipnodes + num;
726
727         plane = t->hull->planes + node->planenum;
728         if (plane->type < 3)
729         {
730                 t1 = p1[plane->type] - plane->dist;
731                 t2 = p2[plane->type] - plane->dist;
732         }
733         else
734         {
735                 t1 = DotProduct (plane->normal, p1) - plane->dist;
736                 t2 = DotProduct (plane->normal, p2) - plane->dist;
737         }
738
739         if (t1 < 0)
740         {
741                 if (t2 < 0)
742                 {
743 #if COLLISIONPARANOID >= 3
744                         Con_Print("<");
745 #endif
746                         num = node->children[1];
747                         goto loc0;
748                 }
749                 side = 1;
750         }
751         else
752         {
753                 if (t2 >= 0)
754                 {
755 #if COLLISIONPARANOID >= 3
756                         Con_Print(">");
757 #endif
758                         num = node->children[0];
759                         goto loc0;
760                 }
761                 side = 0;
762         }
763
764         // the line intersects, find intersection point
765         // LordHavoc: this uses the original trace for maximum accuracy
766 #if COLLISIONPARANOID >= 3
767         Con_Print("M");
768 #endif
769         if (plane->type < 3)
770         {
771                 t1 = t->start[plane->type] - plane->dist;
772                 t2 = t->end[plane->type] - plane->dist;
773         }
774         else
775         {
776                 t1 = DotProduct (plane->normal, t->start) - plane->dist;
777                 t2 = DotProduct (plane->normal, t->end) - plane->dist;
778         }
779
780         midf = t1 / (t1 - t2);
781         midf = bound(p1f, midf, p2f);
782         VectorMA(t->start, midf, t->dist, mid);
783
784         // recurse both sides, front side first
785         ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side], p1f, midf, p1, mid);
786         // if this side is not empty, return what it is (solid or done)
787         if (ret != HULLCHECKSTATE_EMPTY)
788                 return ret;
789
790         ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side ^ 1], midf, p2f, mid, p2);
791         // if other side is not solid, return what it is (empty or done)
792         if (ret != HULLCHECKSTATE_SOLID)
793                 return ret;
794
795         // front is air and back is solid, this is the impact point...
796         if (side)
797         {
798                 t->trace->plane.dist = -plane->dist;
799                 VectorNegate (plane->normal, t->trace->plane.normal);
800         }
801         else
802         {
803                 t->trace->plane.dist = plane->dist;
804                 VectorCopy (plane->normal, t->trace->plane.normal);
805         }
806
807         // calculate the true fraction
808         t1 = DotProduct(t->trace->plane.normal, t->start) - t->trace->plane.dist;
809         t2 = DotProduct(t->trace->plane.normal, t->end) - t->trace->plane.dist;
810         midf = t1 / (t1 - t2);
811         t->trace->realfraction = bound(0, midf, 1);
812
813         // calculate the return fraction which is nudged off the surface a bit
814         midf = (t1 - DIST_EPSILON) / (t1 - t2);
815         t->trace->fraction = bound(0, midf, 1);
816
817         if (collision_prefernudgedfraction.integer)
818                 t->trace->realfraction = t->trace->fraction;
819
820 #if COLLISIONPARANOID >= 3
821         Con_Print("D");
822 #endif
823         return HULLCHECKSTATE_DONE;
824 }
825
826 //#if COLLISIONPARANOID < 2
827 static int Mod_Q1BSP_RecursiveHullCheckPoint(RecursiveHullCheckTraceInfo_t *t, int num)
828 {
829         mplane_t *plane;
830         mclipnode_t *nodes = t->hull->clipnodes;
831         mplane_t *planes = t->hull->planes;
832         vec3_t point;
833         VectorCopy(t->start, point);
834         while (num >= 0)
835         {
836                 plane = planes + nodes[num].planenum;
837                 num = nodes[num].children[(plane->type < 3 ? point[plane->type] : DotProduct(plane->normal, point)) < plane->dist];
838         }
839         num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
840         t->trace->startsupercontents |= num;
841         if (num & SUPERCONTENTS_LIQUIDSMASK)
842                 t->trace->inwater = true;
843         if (num == 0)
844                 t->trace->inopen = true;
845         if (num & t->trace->hitsupercontentsmask)
846         {
847                 t->trace->allsolid = t->trace->startsolid = true;
848                 return HULLCHECKSTATE_SOLID;
849         }
850         else
851         {
852                 t->trace->allsolid = t->trace->startsolid = false;
853                 return HULLCHECKSTATE_EMPTY;
854         }
855 }
856 //#endif
857
858 static void Mod_Q1BSP_TracePoint(struct model_s *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, const vec3_t start, int hitsupercontentsmask)
859 {
860         RecursiveHullCheckTraceInfo_t rhc;
861
862         memset(&rhc, 0, sizeof(rhc));
863         memset(trace, 0, sizeof(trace_t));
864         rhc.trace = trace;
865         rhc.trace->fraction = 1;
866         rhc.trace->realfraction = 1;
867         rhc.trace->allsolid = true;
868         rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
869         VectorCopy(start, rhc.start);
870         VectorCopy(start, rhc.end);
871         Mod_Q1BSP_RecursiveHullCheckPoint(&rhc, rhc.hull->firstclipnode);
872 }
873
874 static void Mod_Q1BSP_TraceLine(struct model_s *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, const vec3_t start, const vec3_t end, int hitsupercontentsmask)
875 {
876         RecursiveHullCheckTraceInfo_t rhc;
877
878         if (VectorCompare(start, end))
879         {
880                 Mod_Q1BSP_TracePoint(model, frameblend, skeleton, trace, start, hitsupercontentsmask);
881                 return;
882         }
883
884         memset(&rhc, 0, sizeof(rhc));
885         memset(trace, 0, sizeof(trace_t));
886         rhc.trace = trace;
887         rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
888         rhc.trace->fraction = 1;
889         rhc.trace->realfraction = 1;
890         rhc.trace->allsolid = true;
891         rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
892         VectorCopy(start, rhc.start);
893         VectorCopy(end, rhc.end);
894         VectorSubtract(rhc.end, rhc.start, rhc.dist);
895 #if COLLISIONPARANOID >= 2
896         Con_Printf("t(%f %f %f,%f %f %f)", rhc.start[0], rhc.start[1], rhc.start[2], rhc.end[0], rhc.end[1], rhc.end[2]);
897         Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
898         {
899
900                 double test[3];
901                 trace_t testtrace;
902                 VectorLerp(rhc.start, rhc.trace->fraction, rhc.end, test);
903                 memset(&testtrace, 0, sizeof(trace_t));
904                 rhc.trace = &testtrace;
905                 rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
906                 rhc.trace->fraction = 1;
907                 rhc.trace->realfraction = 1;
908                 rhc.trace->allsolid = true;
909                 VectorCopy(test, rhc.start);
910                 VectorCopy(test, rhc.end);
911                 VectorClear(rhc.dist);
912                 Mod_Q1BSP_RecursiveHullCheckPoint(&rhc, rhc.hull->firstclipnode);
913                 //Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, test, test);
914                 if (!trace->startsolid && testtrace.startsolid)
915                         Con_Printf(" - ended in solid!\n");
916         }
917         Con_Print("\n");
918 #else
919         if (VectorLength2(rhc.dist))
920                 Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
921         else
922                 Mod_Q1BSP_RecursiveHullCheckPoint(&rhc, rhc.hull->firstclipnode);
923 #endif
924 }
925
926 static void Mod_Q1BSP_TraceBox(struct model_s *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask)
927 {
928         // this function currently only supports same size start and end
929         double boxsize[3];
930         RecursiveHullCheckTraceInfo_t rhc;
931
932         if (VectorCompare(boxmins, boxmaxs))
933         {
934                 if (VectorCompare(start, end))
935                         Mod_Q1BSP_TracePoint(model, frameblend, skeleton, trace, start, hitsupercontentsmask);
936                 else
937                         Mod_Q1BSP_TraceLine(model, frameblend, skeleton, trace, start, end, hitsupercontentsmask);
938                 return;
939         }
940
941         memset(&rhc, 0, sizeof(rhc));
942         memset(trace, 0, sizeof(trace_t));
943         rhc.trace = trace;
944         rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
945         rhc.trace->fraction = 1;
946         rhc.trace->realfraction = 1;
947         rhc.trace->allsolid = true;
948         VectorSubtract(boxmaxs, boxmins, boxsize);
949         if (boxsize[0] < 3)
950                 rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
951         else if (model->brush.ishlbsp)
952         {
953                 // LordHavoc: this has to have a minor tolerance (the .1) because of
954                 // minor float precision errors from the box being transformed around
955                 if (boxsize[0] < 32.1)
956                 {
957                         if (boxsize[2] < 54) // pick the nearest of 36 or 72
958                                 rhc.hull = &model->brushq1.hulls[3]; // 32x32x36
959                         else
960                                 rhc.hull = &model->brushq1.hulls[1]; // 32x32x72
961                 }
962                 else
963                         rhc.hull = &model->brushq1.hulls[2]; // 64x64x64
964         }
965         else
966         {
967                 // LordHavoc: this has to have a minor tolerance (the .1) because of
968                 // minor float precision errors from the box being transformed around
969                 if (boxsize[0] < 32.1)
970                         rhc.hull = &model->brushq1.hulls[1]; // 32x32x56
971                 else
972                         rhc.hull = &model->brushq1.hulls[2]; // 64x64x88
973         }
974         VectorMAMAM(1, start, 1, boxmins, -1, rhc.hull->clip_mins, rhc.start);
975         VectorMAMAM(1, end, 1, boxmins, -1, rhc.hull->clip_mins, rhc.end);
976         VectorSubtract(rhc.end, rhc.start, rhc.dist);
977 #if COLLISIONPARANOID >= 2
978         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]);
979         Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
980         {
981
982                 double test[3];
983                 trace_t testtrace;
984                 VectorLerp(rhc.start, rhc.trace->fraction, rhc.end, test);
985                 memset(&testtrace, 0, sizeof(trace_t));
986                 rhc.trace = &testtrace;
987                 rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
988                 rhc.trace->fraction = 1;
989                 rhc.trace->realfraction = 1;
990                 rhc.trace->allsolid = true;
991                 VectorCopy(test, rhc.start);
992                 VectorCopy(test, rhc.end);
993                 VectorClear(rhc.dist);
994                 Mod_Q1BSP_RecursiveHullCheckPoint(&rhc, rhc.hull->firstclipnode);
995                 //Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, test, test);
996                 if (!trace->startsolid && testtrace.startsolid)
997                         Con_Printf(" - ended in solid!\n");
998         }
999         Con_Print("\n");
1000 #else
1001         if (VectorLength2(rhc.dist))
1002                 Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
1003         else
1004                 Mod_Q1BSP_RecursiveHullCheckPoint(&rhc, rhc.hull->firstclipnode);
1005 #endif
1006 }
1007
1008 static int Mod_Q1BSP_PointSuperContents(struct model_s *model, int frame, const vec3_t point)
1009 {
1010         int num = model->brushq1.hulls[0].firstclipnode;
1011         mplane_t *plane;
1012         mclipnode_t *nodes = model->brushq1.hulls[0].clipnodes;
1013         mplane_t *planes = model->brushq1.hulls[0].planes;
1014         while (num >= 0)
1015         {
1016                 plane = planes + nodes[num].planenum;
1017                 num = nodes[num].children[(plane->type < 3 ? point[plane->type] : DotProduct(plane->normal, point)) < plane->dist];
1018         }
1019         return Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
1020 }
1021
1022 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, const texture_t *boxtexture)
1023 {
1024 #if 1
1025         colbrushf_t cbox;
1026         colplanef_t cbox_planes[6];
1027         cbox.isaabb = true;
1028         cbox.hasaabbplanes = true;
1029         cbox.supercontents = boxsupercontents;
1030         cbox.numplanes = 6;
1031         cbox.numpoints = 0;
1032         cbox.numtriangles = 0;
1033         cbox.planes = cbox_planes;
1034         cbox.points = NULL;
1035         cbox.elements = NULL;
1036         cbox.markframe = 0;
1037         cbox.mins[0] = 0;
1038         cbox.mins[1] = 0;
1039         cbox.mins[2] = 0;
1040         cbox.maxs[0] = 0;
1041         cbox.maxs[1] = 0;
1042         cbox.maxs[2] = 0;
1043         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];
1044         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];
1045         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];
1046         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];
1047         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];
1048         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];
1049         cbox_planes[0].q3surfaceflags = boxq3surfaceflags;cbox_planes[0].texture = boxtexture;
1050         cbox_planes[1].q3surfaceflags = boxq3surfaceflags;cbox_planes[1].texture = boxtexture;
1051         cbox_planes[2].q3surfaceflags = boxq3surfaceflags;cbox_planes[2].texture = boxtexture;
1052         cbox_planes[3].q3surfaceflags = boxq3surfaceflags;cbox_planes[3].texture = boxtexture;
1053         cbox_planes[4].q3surfaceflags = boxq3surfaceflags;cbox_planes[4].texture = boxtexture;
1054         cbox_planes[5].q3surfaceflags = boxq3surfaceflags;cbox_planes[5].texture = boxtexture;
1055         memset(trace, 0, sizeof(trace_t));
1056         trace->hitsupercontentsmask = hitsupercontentsmask;
1057         trace->fraction = 1;
1058         trace->realfraction = 1;
1059         Collision_TraceLineBrushFloat(trace, start, end, &cbox, &cbox);
1060 #else
1061         RecursiveHullCheckTraceInfo_t rhc;
1062         static hull_t box_hull;
1063         static mclipnode_t box_clipnodes[6];
1064         static mplane_t box_planes[6];
1065         // fill in a default trace
1066         memset(&rhc, 0, sizeof(rhc));
1067         memset(trace, 0, sizeof(trace_t));
1068         //To keep everything totally uniform, bounding boxes are turned into small
1069         //BSP trees instead of being compared directly.
1070         // create a temp hull from bounding box sizes
1071         box_planes[0].dist = cmaxs[0] - mins[0];
1072         box_planes[1].dist = cmins[0] - maxs[0];
1073         box_planes[2].dist = cmaxs[1] - mins[1];
1074         box_planes[3].dist = cmins[1] - maxs[1];
1075         box_planes[4].dist = cmaxs[2] - mins[2];
1076         box_planes[5].dist = cmins[2] - maxs[2];
1077 #if COLLISIONPARANOID >= 3
1078         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]);
1079 #endif
1080
1081         if (box_hull.clipnodes == NULL)
1082         {
1083                 int i, side;
1084
1085                 //Set up the planes and clipnodes so that the six floats of a bounding box
1086                 //can just be stored out and get a proper hull_t structure.
1087
1088                 box_hull.clipnodes = box_clipnodes;
1089                 box_hull.planes = box_planes;
1090                 box_hull.firstclipnode = 0;
1091                 box_hull.lastclipnode = 5;
1092
1093                 for (i = 0;i < 6;i++)
1094                 {
1095                         box_clipnodes[i].planenum = i;
1096
1097                         side = i&1;
1098
1099                         box_clipnodes[i].children[side] = CONTENTS_EMPTY;
1100                         if (i != 5)
1101                                 box_clipnodes[i].children[side^1] = i + 1;
1102                         else
1103                                 box_clipnodes[i].children[side^1] = CONTENTS_SOLID;
1104
1105                         box_planes[i].type = i>>1;
1106                         box_planes[i].normal[i>>1] = 1;
1107                 }
1108         }
1109
1110         // trace a line through the generated clipping hull
1111         //rhc.boxsupercontents = boxsupercontents;
1112         rhc.hull = &box_hull;
1113         rhc.trace = trace;
1114         rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
1115         rhc.trace->fraction = 1;
1116         rhc.trace->realfraction = 1;
1117         rhc.trace->allsolid = true;
1118         VectorCopy(start, rhc.start);
1119         VectorCopy(end, rhc.end);
1120         VectorSubtract(rhc.end, rhc.start, rhc.dist);
1121         Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
1122         //VectorMA(rhc.start, rhc.trace->fraction, rhc.dist, rhc.trace->endpos);
1123         if (rhc.trace->startsupercontents)
1124                 rhc.trace->startsupercontents = boxsupercontents;
1125 #endif
1126 }
1127
1128 void Collision_ClipTrace_Point(trace_t *trace, const vec3_t cmins, const vec3_t cmaxs, const vec3_t start, int hitsupercontentsmask, int boxsupercontents, int boxq3surfaceflags, const texture_t *boxtexture)
1129 {
1130         memset(trace, 0, sizeof(trace_t));
1131         trace->fraction = 1;
1132         trace->realfraction = 1;
1133         if (BoxesOverlap(start, start, cmins, cmaxs))
1134         {
1135                 trace->startsupercontents |= boxsupercontents;
1136                 if (hitsupercontentsmask & boxsupercontents)
1137                 {
1138                         trace->startsolid = true;
1139                         trace->allsolid = true;
1140                 }
1141         }
1142 }
1143
1144 static qboolean Mod_Q1BSP_TraceLineOfSight(struct model_s *model, const vec3_t start, const vec3_t end)
1145 {
1146         trace_t trace;
1147         model->TraceLine(model, NULL, NULL, &trace, start, end, SUPERCONTENTS_VISBLOCKERMASK);
1148         return trace.fraction == 1;
1149 }
1150
1151 static int Mod_Q1BSP_LightPoint_RecursiveBSPNode(dp_model_t *model, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal, const mnode_t *node, float x, float y, float startz, float endz)
1152 {
1153         int side;
1154         float front, back;
1155         float mid, distz = endz - startz;
1156
1157 loc0:
1158         if (!node->plane)
1159                 return false;           // didn't hit anything
1160
1161         switch (node->plane->type)
1162         {
1163         case PLANE_X:
1164                 node = node->children[x < node->plane->dist];
1165                 goto loc0;
1166         case PLANE_Y:
1167                 node = node->children[y < node->plane->dist];
1168                 goto loc0;
1169         case PLANE_Z:
1170                 side = startz < node->plane->dist;
1171                 if ((endz < node->plane->dist) == side)
1172                 {
1173                         node = node->children[side];
1174                         goto loc0;
1175                 }
1176                 // found an intersection
1177                 mid = node->plane->dist;
1178                 break;
1179         default:
1180                 back = front = x * node->plane->normal[0] + y * node->plane->normal[1];
1181                 front += startz * node->plane->normal[2];
1182                 back += endz * node->plane->normal[2];
1183                 side = front < node->plane->dist;
1184                 if ((back < node->plane->dist) == side)
1185                 {
1186                         node = node->children[side];
1187                         goto loc0;
1188                 }
1189                 // found an intersection
1190                 mid = startz + distz * (front - node->plane->dist) / (front - back);
1191                 break;
1192         }
1193
1194         // go down front side
1195         if (node->children[side]->plane && Mod_Q1BSP_LightPoint_RecursiveBSPNode(model, ambientcolor, diffusecolor, diffusenormal, node->children[side], x, y, startz, mid))
1196                 return true;    // hit something
1197         else
1198         {
1199                 // check for impact on this node
1200                 if (node->numsurfaces)
1201                 {
1202                         int i, dsi, dti, lmwidth, lmheight;
1203                         float ds, dt;
1204                         msurface_t *surface;
1205                         unsigned char *lightmap;
1206                         int maps, line3, size3;
1207                         float dsfrac;
1208                         float dtfrac;
1209                         float scale, w, w00, w01, w10, w11;
1210
1211                         surface = model->data_surfaces + node->firstsurface;
1212                         for (i = 0;i < node->numsurfaces;i++, surface++)
1213                         {
1214                                 if (!(surface->texture->basematerialflags & MATERIALFLAG_WALL) || !surface->lightmapinfo || !surface->lightmapinfo->samples)
1215                                         continue;       // no lightmaps
1216
1217                                 // location we want to sample in the lightmap
1218                                 ds = ((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]) * 0.0625f;
1219                                 dt = ((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]) * 0.0625f;
1220
1221                                 // check the bounds
1222                                 dsi = (int)ds;
1223                                 dti = (int)dt;
1224                                 lmwidth = ((surface->lightmapinfo->extents[0]>>4)+1);
1225                                 lmheight = ((surface->lightmapinfo->extents[1]>>4)+1);
1226
1227                                 // is it in bounds?
1228                                 if (dsi >= 0 && dsi < lmwidth-1 && dti >= 0 && dti < lmheight-1)
1229                                 {
1230                                         // calculate bilinear interpolation factors
1231                                         // and also multiply by fixedpoint conversion factors
1232                                         dsfrac = ds - dsi;
1233                                         dtfrac = dt - dti;
1234                                         w00 = (1 - dsfrac) * (1 - dtfrac) * (1.0f / 32768.0f);
1235                                         w01 = (    dsfrac) * (1 - dtfrac) * (1.0f / 32768.0f);
1236                                         w10 = (1 - dsfrac) * (    dtfrac) * (1.0f / 32768.0f);
1237                                         w11 = (    dsfrac) * (    dtfrac) * (1.0f / 32768.0f);
1238
1239                                         // values for pointer math
1240                                         line3 = lmwidth * 3; // LordHavoc: *3 for colored lighting
1241                                         size3 = lmwidth * lmheight * 3; // LordHavoc: *3 for colored lighting
1242
1243                                         // look up the pixel
1244                                         lightmap = surface->lightmapinfo->samples + dti * line3 + dsi*3; // LordHavoc: *3 for colored lighting
1245
1246                                         // bilinear filter each lightmap style, and sum them
1247                                         for (maps = 0;maps < MAXLIGHTMAPS && surface->lightmapinfo->styles[maps] != 255;maps++)
1248                                         {
1249                                                 scale = r_refdef.scene.lightstylevalue[surface->lightmapinfo->styles[maps]];
1250                                                 w = w00 * scale;VectorMA(ambientcolor, w, lightmap            , ambientcolor);
1251                                                 w = w01 * scale;VectorMA(ambientcolor, w, lightmap + 3        , ambientcolor);
1252                                                 w = w10 * scale;VectorMA(ambientcolor, w, lightmap + line3    , ambientcolor);
1253                                                 w = w11 * scale;VectorMA(ambientcolor, w, lightmap + line3 + 3, ambientcolor);
1254                                                 lightmap += size3;
1255                                         }
1256
1257                                         return true; // success
1258                                 }
1259                         }
1260                 }
1261
1262                 // go down back side
1263                 node = node->children[side ^ 1];
1264                 startz = mid;
1265                 distz = endz - startz;
1266                 goto loc0;
1267         }
1268 }
1269
1270 void Mod_Q1BSP_LightPoint(dp_model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
1271 {
1272         // pretend lighting is coming down from above (due to lack of a lightgrid to know primary lighting direction)
1273         VectorSet(diffusenormal, 0, 0, 1);
1274
1275         if (!model->brushq1.lightdata)
1276         {
1277                 VectorSet(ambientcolor, 1, 1, 1);
1278                 VectorSet(diffusecolor, 0, 0, 0);
1279                 return;
1280         }
1281
1282         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);
1283 }
1284
1285 static void Mod_Q1BSP_DecompressVis(const unsigned char *in, const unsigned char *inend, unsigned char *out, unsigned char *outend)
1286 {
1287         int c;
1288         unsigned char *outstart = out;
1289         while (out < outend)
1290         {
1291                 if (in == inend)
1292                 {
1293                         Con_Printf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, (int)(out - outstart), (int)(outend - outstart));
1294                         return;
1295                 }
1296                 c = *in++;
1297                 if (c)
1298                         *out++ = c;
1299                 else
1300                 {
1301                         if (in == inend)
1302                         {
1303                                 Con_Printf("Mod_Q1BSP_DecompressVis: input underrun (during zero-run) on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, (int)(out - outstart), (int)(outend - outstart));
1304                                 return;
1305                         }
1306                         for (c = *in++;c > 0;c--)
1307                         {
1308                                 if (out == outend)
1309                                 {
1310                                         Con_Printf("Mod_Q1BSP_DecompressVis: output overrun on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, (int)(out - outstart), (int)(outend - outstart));
1311                                         return;
1312                                 }
1313                                 *out++ = 0;
1314                         }
1315                 }
1316         }
1317 }
1318
1319 /*
1320 =============
1321 R_Q1BSP_LoadSplitSky
1322
1323 A sky texture is 256*128, with the right side being a masked overlay
1324 ==============
1325 */
1326 void R_Q1BSP_LoadSplitSky (unsigned char *src, int width, int height, int bytesperpixel)
1327 {
1328         int x, y;
1329         int w = width/2;
1330         int h = height;
1331         unsigned *solidpixels = Mem_Alloc(tempmempool, w*h*sizeof(unsigned char[4]));
1332         unsigned *alphapixels = Mem_Alloc(tempmempool, w*h*sizeof(unsigned char[4]));
1333
1334         // allocate a texture pool if we need it
1335         if (loadmodel->texturepool == NULL && cls.state != ca_dedicated)
1336                 loadmodel->texturepool = R_AllocTexturePool();
1337
1338         if (bytesperpixel == 4)
1339         {
1340                 for (y = 0;y < h;y++)
1341                 {
1342                         for (x = 0;x < w;x++)
1343                         {
1344                                 solidpixels[y*w+x] = ((unsigned *)src)[y*width+x+w];
1345                                 alphapixels[y*w+x] = ((unsigned *)src)[y*width+x];
1346                         }
1347                 }
1348         }
1349         else
1350         {
1351                 // make an average value for the back to avoid
1352                 // a fringe on the top level
1353                 int p, r, g, b;
1354                 union
1355                 {
1356                         unsigned int i;
1357                         unsigned char b[4];
1358                 }
1359                 bgra;
1360                 r = g = b = 0;
1361                 for (y = 0;y < h;y++)
1362                 {
1363                         for (x = 0;x < w;x++)
1364                         {
1365                                 p = src[x*width+y+w];
1366                                 r += palette_rgb[p][0];
1367                                 g += palette_rgb[p][1];
1368                                 b += palette_rgb[p][2];
1369                         }
1370                 }
1371                 bgra.b[2] = r/(w*h);
1372                 bgra.b[1] = g/(w*h);
1373                 bgra.b[0] = b/(w*h);
1374                 bgra.b[3] = 0;
1375                 for (y = 0;y < h;y++)
1376                 {
1377                         for (x = 0;x < w;x++)
1378                         {
1379                                 solidpixels[y*w+x] = palette_bgra_complete[src[y*width+x+w]];
1380                                 p = src[y*width+x];
1381                                 alphapixels[y*w+x] = p ? palette_bgra_complete[p] : bgra.i;
1382                         }
1383                 }
1384         }
1385
1386         loadmodel->brush.solidskyskinframe = R_SkinFrame_LoadInternalBGRA("sky_solidtexture", 0         , (unsigned char *) solidpixels, w, h);
1387         loadmodel->brush.alphaskyskinframe = R_SkinFrame_LoadInternalBGRA("sky_alphatexture", TEXF_ALPHA, (unsigned char *) alphapixels, w, h);
1388         Mem_Free(solidpixels);
1389         Mem_Free(alphapixels);
1390 }
1391
1392 static void Mod_Q1BSP_LoadTextures(lump_t *l)
1393 {
1394         int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
1395         skinframe_t *skinframe;
1396         miptex_t *dmiptex;
1397         texture_t *tx, *tx2, *anims[10], *altanims[10];
1398         dmiptexlump_t *m;
1399         unsigned char *data, *mtdata;
1400         const char *s;
1401         char mapname[MAX_QPATH], name[MAX_QPATH];
1402         unsigned char zero[4];
1403
1404         memset(zero, 0, sizeof(zero));
1405
1406         loadmodel->data_textures = NULL;
1407
1408         // add two slots for notexture walls and notexture liquids
1409         if (l->filelen)
1410         {
1411                 m = (dmiptexlump_t *)(mod_base + l->fileofs);
1412                 m->nummiptex = LittleLong (m->nummiptex);
1413                 loadmodel->num_textures = m->nummiptex + 2;
1414                 loadmodel->num_texturesperskin = loadmodel->num_textures;
1415         }
1416         else
1417         {
1418                 m = NULL;
1419                 loadmodel->num_textures = 2;
1420                 loadmodel->num_texturesperskin = loadmodel->num_textures;
1421         }
1422
1423         loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_textures * sizeof(texture_t));
1424
1425         // fill out all slots with notexture
1426         if (cls.state != ca_dedicated)
1427                 skinframe = R_SkinFrame_LoadMissing();
1428         else
1429                 skinframe = NULL;
1430         for (i = 0, tx = loadmodel->data_textures;i < loadmodel->num_textures;i++, tx++)
1431         {
1432                 strlcpy(tx->name, "NO TEXTURE FOUND", sizeof(tx->name));
1433                 tx->width = 16;
1434                 tx->height = 16;
1435                 if (cls.state != ca_dedicated)
1436                 {
1437                         tx->numskinframes = 1;
1438                         tx->skinframerate = 1;
1439                         tx->skinframes[0] = skinframe;
1440                         tx->currentskinframe = tx->skinframes[0];
1441                 }
1442                 tx->basematerialflags = MATERIALFLAG_WALL;
1443                 if (i == loadmodel->num_textures - 1)
1444                 {
1445                         tx->basematerialflags |= MATERIALFLAG_WATERSCROLL | MATERIALFLAG_LIGHTBOTHSIDES | MATERIALFLAG_NOSHADOW;
1446                         tx->supercontents = mod_q1bsp_texture_water.supercontents;
1447                         tx->surfaceflags = mod_q1bsp_texture_water.surfaceflags;
1448                 }
1449                 else
1450                 {
1451                         tx->supercontents = mod_q1bsp_texture_solid.supercontents;
1452                         tx->surfaceflags = mod_q1bsp_texture_solid.surfaceflags;
1453                 }
1454                 tx->currentframe = tx;
1455
1456                 // clear water settings
1457                 tx->reflectmin = 0;
1458                 tx->reflectmax = 1;
1459                 tx->refractfactor = 1;
1460                 Vector4Set(tx->refractcolor4f, 1, 1, 1, 1);
1461                 tx->reflectfactor = 1;
1462                 Vector4Set(tx->reflectcolor4f, 1, 1, 1, 1);
1463                 tx->r_water_wateralpha = 1;
1464                 tx->specularscalemod = 1;
1465                 tx->specularpowermod = 1;
1466         }
1467
1468         if (!m)
1469         {
1470                 Con_Printf("%s: no miptex lump to load textures from\n", loadmodel->name);
1471                 return;
1472         }
1473
1474         s = loadmodel->name;
1475         if (!strncasecmp(s, "maps/", 5))
1476                 s += 5;
1477         FS_StripExtension(s, mapname, sizeof(mapname));
1478
1479         // just to work around bounds checking when debugging with it (array index out of bounds error thing)
1480         dofs = m->dataofs;
1481         // LordHavoc: mostly rewritten map texture loader
1482         for (i = 0;i < m->nummiptex;i++)
1483         {
1484                 dofs[i] = LittleLong(dofs[i]);
1485                 if (r_nosurftextures.integer)
1486                         continue;
1487                 if (dofs[i] == -1)
1488                 {
1489                         Con_DPrintf("%s: miptex #%i missing\n", loadmodel->name, i);
1490                         continue;
1491                 }
1492                 dmiptex = (miptex_t *)((unsigned char *)m + dofs[i]);
1493
1494                 // copy name, but only up to 16 characters
1495                 // (the output buffer can hold more than this, but the input buffer is
1496                 //  only 16)
1497                 for (j = 0;j < 16 && dmiptex->name[j];j++)
1498                         name[j] = dmiptex->name[j];
1499                 name[j] = 0;
1500
1501                 if (!name[0])
1502                 {
1503                         dpsnprintf(name, sizeof(name), "unnamed%i", i);
1504                         Con_DPrintf("%s: warning: renaming unnamed texture to %s\n", loadmodel->name, name);
1505                 }
1506
1507                 mtwidth = LittleLong(dmiptex->width);
1508                 mtheight = LittleLong(dmiptex->height);
1509                 mtdata = NULL;
1510                 j = LittleLong(dmiptex->offsets[0]);
1511                 if (j)
1512                 {
1513                         // texture included
1514                         if (j < 40 || j + mtwidth * mtheight > l->filelen)
1515                         {
1516                                 Con_Printf("%s: Texture \"%s\" is corrupt or incomplete\n", loadmodel->name, dmiptex->name);
1517                                 continue;
1518                         }
1519                         mtdata = (unsigned char *)dmiptex + j;
1520                 }
1521
1522                 if ((mtwidth & 15) || (mtheight & 15))
1523                         Con_DPrintf("%s: warning: texture \"%s\" is not 16 aligned\n", loadmodel->name, dmiptex->name);
1524
1525                 // LordHavoc: force all names to lowercase
1526                 for (j = 0;name[j];j++)
1527                         if (name[j] >= 'A' && name[j] <= 'Z')
1528                                 name[j] += 'a' - 'A';
1529
1530                 if (dmiptex->name[0] && Mod_LoadTextureFromQ3Shader(loadmodel->data_textures + i, name, false, false, 0))
1531                         continue;
1532
1533                 tx = loadmodel->data_textures + i;
1534                 strlcpy(tx->name, name, sizeof(tx->name));
1535                 tx->width = mtwidth;
1536                 tx->height = mtheight;
1537
1538                 if (tx->name[0] == '*')
1539                 {
1540                         if (!strncmp(tx->name, "*lava", 5))
1541                         {
1542                                 tx->supercontents = mod_q1bsp_texture_lava.supercontents;
1543                                 tx->surfaceflags = mod_q1bsp_texture_lava.surfaceflags;
1544                         }
1545                         else if (!strncmp(tx->name, "*slime", 6))
1546                         {
1547                                 tx->supercontents = mod_q1bsp_texture_slime.supercontents;
1548                                 tx->surfaceflags = mod_q1bsp_texture_slime.surfaceflags;
1549                         }
1550                         else
1551                         {
1552                                 tx->supercontents = mod_q1bsp_texture_water.supercontents;
1553                                 tx->surfaceflags = mod_q1bsp_texture_water.surfaceflags;
1554                         }
1555                 }
1556                 else if (!strncmp(tx->name, "sky", 3))
1557                 {
1558                         tx->supercontents = mod_q1bsp_texture_sky.supercontents;
1559                         tx->surfaceflags = mod_q1bsp_texture_sky.surfaceflags;
1560                 }
1561                 else
1562                 {
1563                         tx->supercontents = mod_q1bsp_texture_solid.supercontents;
1564                         tx->surfaceflags = mod_q1bsp_texture_solid.surfaceflags;
1565                 }
1566
1567                 if (cls.state != ca_dedicated)
1568                 {
1569                         // LordHavoc: HL sky textures are entirely different than quake
1570                         if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == mtheight * 2)
1571                         {
1572                                 data = loadimagepixelsbgra(tx->name, false, false, r_texture_convertsRGB_skin.integer);
1573                                 if (data && image_width == image_height * 2)
1574                                 {
1575                                         R_Q1BSP_LoadSplitSky(data, image_width, image_height, 4);
1576                                         Mem_Free(data);
1577                                 }
1578                                 else if (mtdata != NULL)
1579                                         R_Q1BSP_LoadSplitSky(mtdata, mtwidth, mtheight, 1);
1580                         }
1581                         else
1582                         {
1583                                 skinframe = R_SkinFrame_LoadExternal(gamemode == GAME_TENEBRAE ? tx->name : va("textures/%s/%s", mapname, tx->name), TEXF_ALPHA | TEXF_MIPMAP | (r_picmipworld.integer ? TEXF_PICMIP : 0) | TEXF_COMPRESS, false);
1584                                 if (!skinframe)
1585                                         skinframe = R_SkinFrame_LoadExternal(gamemode == GAME_TENEBRAE ? tx->name : va("textures/%s", tx->name), TEXF_ALPHA | TEXF_MIPMAP | (r_picmipworld.integer ? TEXF_PICMIP : 0) | TEXF_COMPRESS, false);
1586                                 if (!skinframe)
1587                                 {
1588                                         // did not find external texture, load it from the bsp or wad3
1589                                         if (loadmodel->brush.ishlbsp)
1590                                         {
1591                                                 // internal texture overrides wad
1592                                                 unsigned char *pixels, *freepixels;
1593                                                 pixels = freepixels = NULL;
1594                                                 if (mtdata)
1595                                                         pixels = W_ConvertWAD3TextureBGRA(dmiptex);
1596                                                 if (pixels == NULL)
1597                                                         pixels = freepixels = W_GetTextureBGRA(tx->name);
1598                                                 if (pixels != NULL)
1599                                                 {
1600                                                         tx->width = image_width;
1601                                                         tx->height = image_height;
1602                                                         skinframe = R_SkinFrame_LoadInternalBGRA(tx->name, TEXF_ALPHA | TEXF_MIPMAP | (r_picmipworld.integer ? TEXF_PICMIP : 0), pixels, image_width, image_height);
1603                                                 }
1604                                                 if (freepixels)
1605                                                         Mem_Free(freepixels);
1606                                         }
1607                                         else if (mtdata) // texture included
1608                                                 skinframe = R_SkinFrame_LoadInternalQuake(tx->name, TEXF_MIPMAP | (r_picmipworld.integer ? TEXF_PICMIP : 0), false, r_fullbrights.integer, mtdata, tx->width, tx->height);
1609                                 }
1610                                 // if skinframe is still NULL the "missing" texture will be used
1611                                 if (skinframe)
1612                                         tx->skinframes[0] = skinframe;
1613                         }
1614
1615                         tx->basematerialflags = MATERIALFLAG_WALL;
1616                         if (tx->name[0] == '*')
1617                         {
1618                                 // LordHavoc: some turbulent textures should not be affected by wateralpha
1619                                 if (!strncmp(tx->name, "*glassmirror", 12)) // Tenebrae
1620                                 {
1621                                         // replace the texture with transparent black
1622                                         Vector4Set(zero, 128, 128, 128, 128);
1623                                         tx->skinframes[0] = R_SkinFrame_LoadInternalBGRA(tx->name, TEXF_MIPMAP | TEXF_ALPHA, zero, 1, 1);
1624                                         tx->basematerialflags |= MATERIALFLAG_NOSHADOW | MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_REFLECTION;
1625                                 }
1626                                 else if (!strncmp(tx->name,"*lava",5)
1627                                  || !strncmp(tx->name,"*teleport",9)
1628                                  || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
1629                                         tx->basematerialflags |= MATERIALFLAG_WATERSCROLL | MATERIALFLAG_LIGHTBOTHSIDES | MATERIALFLAG_NOSHADOW;
1630                                 else
1631                                         tx->basematerialflags |= MATERIALFLAG_WATERSCROLL | MATERIALFLAG_LIGHTBOTHSIDES | MATERIALFLAG_NOSHADOW | MATERIALFLAG_WATERALPHA | MATERIALFLAG_WATERSHADER;
1632                                 if (tx->skinframes[0] && tx->skinframes[0]->hasalpha)
1633                                         tx->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
1634                         }
1635                         else if (!strncmp(tx->name, "mirror", 6)) // Tenebrae
1636                         {
1637                                 // replace the texture with black
1638                                 tx->skinframes[0] = R_SkinFrame_LoadInternalBGRA(tx->name, 0, zero, 1, 1);
1639                                 tx->basematerialflags |= MATERIALFLAG_REFLECTION;
1640                         }
1641                         else if (!strncmp(tx->name, "sky", 3))
1642                                 tx->basematerialflags = MATERIALFLAG_SKY | MATERIALFLAG_NOSHADOW;
1643                         else if (!strcmp(tx->name, "caulk"))
1644                                 tx->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW;
1645                         else if (tx->skinframes[0] && tx->skinframes[0]->hasalpha)
1646                                 tx->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
1647
1648                         // start out with no animation
1649                         tx->currentframe = tx;
1650                         tx->currentskinframe = tx->skinframes[0];
1651                 }
1652         }
1653
1654         // sequence the animations
1655         for (i = 0;i < m->nummiptex;i++)
1656         {
1657                 tx = loadmodel->data_textures + i;
1658                 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
1659                         continue;
1660                 if (tx->anim_total[0] || tx->anim_total[1])
1661                         continue;       // already sequenced
1662
1663                 // find the number of frames in the animation
1664                 memset(anims, 0, sizeof(anims));
1665                 memset(altanims, 0, sizeof(altanims));
1666
1667                 for (j = i;j < m->nummiptex;j++)
1668                 {
1669                         tx2 = loadmodel->data_textures + j;
1670                         if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
1671                                 continue;
1672
1673                         num = tx2->name[1];
1674                         if (num >= '0' && num <= '9')
1675                                 anims[num - '0'] = tx2;
1676                         else if (num >= 'a' && num <= 'j')
1677                                 altanims[num - 'a'] = tx2;
1678                         else
1679                                 Con_Printf("Bad animating texture %s\n", tx->name);
1680                 }
1681
1682                 max = altmax = 0;
1683                 for (j = 0;j < 10;j++)
1684                 {
1685                         if (anims[j])
1686                                 max = j + 1;
1687                         if (altanims[j])
1688                                 altmax = j + 1;
1689                 }
1690                 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
1691
1692                 incomplete = false;
1693                 for (j = 0;j < max;j++)
1694                 {
1695                         if (!anims[j])
1696                         {
1697                                 Con_Printf("Missing frame %i of %s\n", j, tx->name);
1698                                 incomplete = true;
1699                         }
1700                 }
1701                 for (j = 0;j < altmax;j++)
1702                 {
1703                         if (!altanims[j])
1704                         {
1705                                 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
1706                                 incomplete = true;
1707                         }
1708                 }
1709                 if (incomplete)
1710                         continue;
1711
1712                 if (altmax < 1)
1713                 {
1714                         // if there is no alternate animation, duplicate the primary
1715                         // animation into the alternate
1716                         altmax = max;
1717                         for (k = 0;k < 10;k++)
1718                                 altanims[k] = anims[k];
1719                 }
1720
1721                 // link together the primary animation
1722                 for (j = 0;j < max;j++)
1723                 {
1724                         tx2 = anims[j];
1725                         tx2->animated = true;
1726                         tx2->anim_total[0] = max;
1727                         tx2->anim_total[1] = altmax;
1728                         for (k = 0;k < 10;k++)
1729                         {
1730                                 tx2->anim_frames[0][k] = anims[k];
1731                                 tx2->anim_frames[1][k] = altanims[k];
1732                         }
1733                 }
1734
1735                 // if there really is an alternate anim...
1736                 if (anims[0] != altanims[0])
1737                 {
1738                         // link together the alternate animation
1739                         for (j = 0;j < altmax;j++)
1740                         {
1741                                 tx2 = altanims[j];
1742                                 tx2->animated = true;
1743                                 // the primary/alternate are reversed here
1744                                 tx2->anim_total[0] = altmax;
1745                                 tx2->anim_total[1] = max;
1746                                 for (k = 0;k < 10;k++)
1747                                 {
1748                                         tx2->anim_frames[0][k] = altanims[k];
1749                                         tx2->anim_frames[1][k] = anims[k];
1750                                 }
1751                         }
1752                 }
1753         }
1754 }
1755
1756 static void Mod_Q1BSP_LoadLighting(lump_t *l)
1757 {
1758         int i;
1759         unsigned char *in, *out, *data, d;
1760         char litfilename[MAX_QPATH];
1761         char dlitfilename[MAX_QPATH];
1762         fs_offset_t filesize;
1763         if (loadmodel->brush.ishlbsp) // LordHavoc: load the colored lighting data straight
1764         {
1765                 loadmodel->brushq1.lightdata = (unsigned char *)Mem_Alloc(loadmodel->mempool, l->filelen);
1766                 for (i=0; i<l->filelen; i++)
1767                         loadmodel->brushq1.lightdata[i] = mod_base[l->fileofs+i] >>= 1;
1768         }
1769         else // LordHavoc: bsp version 29 (normal white lighting)
1770         {
1771                 // LordHavoc: hope is not lost yet, check for a .lit file to load
1772                 strlcpy (litfilename, loadmodel->name, sizeof (litfilename));
1773                 FS_StripExtension (litfilename, litfilename, sizeof (litfilename));
1774                 strlcpy (dlitfilename, litfilename, sizeof (dlitfilename));
1775                 strlcat (litfilename, ".lit", sizeof (litfilename));
1776                 strlcat (dlitfilename, ".dlit", sizeof (dlitfilename));
1777                 data = (unsigned char*) FS_LoadFile(litfilename, tempmempool, false, &filesize);
1778                 if (data)
1779                 {
1780                         if (filesize == (fs_offset_t)(8 + l->filelen * 3) && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
1781                         {
1782                                 i = LittleLong(((int *)data)[1]);
1783                                 if (i == 1)
1784                                 {
1785                                         if (developer_loading.integer)
1786                                                 Con_Printf("loaded %s\n", litfilename);
1787                                         loadmodel->brushq1.lightdata = (unsigned char *)Mem_Alloc(loadmodel->mempool, filesize - 8);
1788                                         memcpy(loadmodel->brushq1.lightdata, data + 8, filesize - 8);
1789                                         Mem_Free(data);
1790                                         data = (unsigned char*) FS_LoadFile(dlitfilename, tempmempool, false, &filesize);
1791                                         if (data)
1792                                         {
1793                                                 if (filesize == (fs_offset_t)(8 + l->filelen * 3) && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
1794                                                 {
1795                                                         i = LittleLong(((int *)data)[1]);
1796                                                         if (i == 1)
1797                                                         {
1798                                                                 if (developer_loading.integer)
1799                                                                         Con_Printf("loaded %s\n", dlitfilename);
1800                                                                 loadmodel->brushq1.nmaplightdata = (unsigned char *)Mem_Alloc(loadmodel->mempool, filesize - 8);
1801                                                                 memcpy(loadmodel->brushq1.nmaplightdata, data + 8, filesize - 8);
1802                                                                 loadmodel->brushq3.deluxemapping_modelspace = false;
1803                                                                 loadmodel->brushq3.deluxemapping = true;
1804                                                         }
1805                                                 }
1806                                                 Mem_Free(data);
1807                                                 data = NULL;
1808                                         }
1809                                         return;
1810                                 }
1811                                 else
1812                                         Con_Printf("Unknown .lit file version (%d)\n", i);
1813                         }
1814                         else if (filesize == 8)
1815                                 Con_Print("Empty .lit file, ignoring\n");
1816                         else
1817                                 Con_Printf("Corrupt .lit file (file size %i bytes, should be %i bytes), ignoring\n", (int) filesize, (int) (8 + l->filelen * 3));
1818                         if (data)
1819                         {
1820                                 Mem_Free(data);
1821                                 data = NULL;
1822                         }
1823                 }
1824                 // LordHavoc: oh well, expand the white lighting data
1825                 if (!l->filelen)
1826                         return;
1827                 loadmodel->brushq1.lightdata = (unsigned char *)Mem_Alloc(loadmodel->mempool, l->filelen*3);
1828                 in = mod_base + l->fileofs;
1829                 out = loadmodel->brushq1.lightdata;
1830                 for (i = 0;i < l->filelen;i++)
1831                 {
1832                         d = *in++;
1833                         *out++ = d;
1834                         *out++ = d;
1835                         *out++ = d;
1836                 }
1837         }
1838 }
1839
1840 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1841 {
1842         loadmodel->brushq1.num_compressedpvs = 0;
1843         loadmodel->brushq1.data_compressedpvs = NULL;
1844         if (!l->filelen)
1845                 return;
1846         loadmodel->brushq1.num_compressedpvs = l->filelen;
1847         loadmodel->brushq1.data_compressedpvs = (unsigned char *)Mem_Alloc(loadmodel->mempool, l->filelen);
1848         memcpy(loadmodel->brushq1.data_compressedpvs, mod_base + l->fileofs, l->filelen);
1849 }
1850
1851 // used only for HalfLife maps
1852 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1853 {
1854         char key[128], value[4096];
1855         int i, j, k;
1856         if (!data)
1857                 return;
1858         if (!COM_ParseToken_Simple(&data, false, false))
1859                 return; // error
1860         if (com_token[0] != '{')
1861                 return; // error
1862         while (1)
1863         {
1864                 if (!COM_ParseToken_Simple(&data, false, false))
1865                         return; // error
1866                 if (com_token[0] == '}')
1867                         break; // end of worldspawn
1868                 if (com_token[0] == '_')
1869                         strlcpy(key, com_token + 1, sizeof(key));
1870                 else
1871                         strlcpy(key, com_token, sizeof(key));
1872                 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1873                         key[strlen(key)-1] = 0;
1874                 if (!COM_ParseToken_Simple(&data, false, false))
1875                         return; // error
1876                 dpsnprintf(value, sizeof(value), "%s", com_token);
1877                 if (!strcmp("wad", key)) // for HalfLife maps
1878                 {
1879                         if (loadmodel->brush.ishlbsp)
1880                         {
1881                                 j = 0;
1882                                 for (i = 0;i < (int)sizeof(value);i++)
1883                                         if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1884                                                 break;
1885                                 if (value[i])
1886                                 {
1887                                         for (;i < (int)sizeof(value);i++)
1888                                         {
1889                                                 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1890                                                 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1891                                                         j = i+1;
1892                                                 else if (value[i] == ';' || value[i] == 0)
1893                                                 {
1894                                                         k = value[i];
1895                                                         value[i] = 0;
1896                                                         W_LoadTextureWadFile(&value[j], false);
1897                                                         j = i+1;
1898                                                         if (!k)
1899                                                                 break;
1900                                                 }
1901                                         }
1902                                 }
1903                         }
1904                 }
1905         }
1906 }
1907
1908 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1909 {
1910         loadmodel->brush.entities = NULL;
1911         if (!l->filelen)
1912                 return;
1913         loadmodel->brush.entities = (char *)Mem_Alloc(loadmodel->mempool, l->filelen + 1);
1914         memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1915         loadmodel->brush.entities[l->filelen] = 0;
1916         if (loadmodel->brush.ishlbsp)
1917                 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1918 }
1919
1920
1921 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1922 {
1923         dvertex_t       *in;
1924         mvertex_t       *out;
1925         int                     i, count;
1926
1927         in = (dvertex_t *)(mod_base + l->fileofs);
1928         if (l->filelen % sizeof(*in))
1929                 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1930         count = l->filelen / sizeof(*in);
1931         out = (mvertex_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1932
1933         loadmodel->brushq1.vertexes = out;
1934         loadmodel->brushq1.numvertexes = count;
1935
1936         for ( i=0 ; i<count ; i++, in++, out++)
1937         {
1938                 out->position[0] = LittleFloat(in->point[0]);
1939                 out->position[1] = LittleFloat(in->point[1]);
1940                 out->position[2] = LittleFloat(in->point[2]);
1941         }
1942 }
1943
1944 // The following two functions should be removed and MSG_* or SZ_* function sets adjusted so they
1945 // can be used for this
1946 // REMOVEME
1947 int SB_ReadInt (unsigned char **buffer)
1948 {
1949         int     i;
1950         i = ((*buffer)[0]) + 256*((*buffer)[1]) + 65536*((*buffer)[2]) + 16777216*((*buffer)[3]);
1951         (*buffer) += 4;
1952         return i;
1953 }
1954
1955 // REMOVEME
1956 float SB_ReadFloat (unsigned char **buffer)
1957 {
1958         union
1959         {
1960                 int             i;
1961                 float   f;
1962         } u;
1963
1964         u.i = SB_ReadInt (buffer);
1965         return u.f;
1966 }
1967
1968 static void Mod_Q1BSP_LoadSubmodels(lump_t *l, hullinfo_t *hullinfo)
1969 {
1970         unsigned char           *index;
1971         dmodel_t        *out;
1972         int                     i, j, count;
1973
1974         index = (unsigned char *)(mod_base + l->fileofs);
1975         if (l->filelen % (48+4*hullinfo->filehulls))
1976                 Host_Error ("Mod_Q1BSP_LoadSubmodels: funny lump size in %s", loadmodel->name);
1977
1978         count = l->filelen / (48+4*hullinfo->filehulls);
1979         out = (dmodel_t *)Mem_Alloc (loadmodel->mempool, count*sizeof(*out));
1980
1981         loadmodel->brushq1.submodels = out;
1982         loadmodel->brush.numsubmodels = count;
1983
1984         for (i = 0; i < count; i++, out++)
1985         {
1986         // spread out the mins / maxs by a pixel
1987                 out->mins[0] = SB_ReadFloat (&index) - 1;
1988                 out->mins[1] = SB_ReadFloat (&index) - 1;
1989                 out->mins[2] = SB_ReadFloat (&index) - 1;
1990                 out->maxs[0] = SB_ReadFloat (&index) + 1;
1991                 out->maxs[1] = SB_ReadFloat (&index) + 1;
1992                 out->maxs[2] = SB_ReadFloat (&index) + 1;
1993                 out->origin[0] = SB_ReadFloat (&index);
1994                 out->origin[1] = SB_ReadFloat (&index);
1995                 out->origin[2] = SB_ReadFloat (&index);
1996                 for (j = 0; j < hullinfo->filehulls; j++)
1997                         out->headnode[j] = SB_ReadInt (&index);
1998                 out->visleafs = SB_ReadInt (&index);
1999                 out->firstface = SB_ReadInt (&index);
2000                 out->numfaces = SB_ReadInt (&index);
2001         }
2002 }
2003
2004 static void Mod_Q1BSP_LoadEdges(lump_t *l)
2005 {
2006         dedge_t *in;
2007         medge_t *out;
2008         int     i, count;
2009
2010         in = (dedge_t *)(mod_base + l->fileofs);
2011         if (l->filelen % sizeof(*in))
2012                 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
2013         count = l->filelen / sizeof(*in);
2014         out = (medge_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2015
2016         loadmodel->brushq1.edges = out;
2017         loadmodel->brushq1.numedges = count;
2018
2019         for ( i=0 ; i<count ; i++, in++, out++)
2020         {
2021                 out->v[0] = (unsigned short)LittleShort(in->v[0]);
2022                 out->v[1] = (unsigned short)LittleShort(in->v[1]);
2023                 if (out->v[0] >= loadmodel->brushq1.numvertexes || out->v[1] >= loadmodel->brushq1.numvertexes)
2024                 {
2025                         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);
2026                         if(!loadmodel->brushq1.numvertexes)
2027                                 Host_Error("Mod_Q1BSP_LoadEdges: %s has edges but no vertexes, cannot fix\n", loadmodel->name);
2028                                 
2029                         out->v[0] = 0;
2030                         out->v[1] = 0;
2031                 }
2032         }
2033 }
2034
2035 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
2036 {
2037         texinfo_t *in;
2038         mtexinfo_t *out;
2039         int i, j, k, count, miptex;
2040
2041         in = (texinfo_t *)(mod_base + l->fileofs);
2042         if (l->filelen % sizeof(*in))
2043                 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
2044         count = l->filelen / sizeof(*in);
2045         out = (mtexinfo_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2046
2047         loadmodel->brushq1.texinfo = out;
2048         loadmodel->brushq1.numtexinfo = count;
2049
2050         for (i = 0;i < count;i++, in++, out++)
2051         {
2052                 for (k = 0;k < 2;k++)
2053                         for (j = 0;j < 4;j++)
2054                                 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
2055
2056                 miptex = LittleLong(in->miptex);
2057                 out->flags = LittleLong(in->flags);
2058
2059                 out->texture = NULL;
2060                 if (loadmodel->data_textures)
2061                 {
2062                         if ((unsigned int) miptex >= (unsigned int) loadmodel->num_textures)
2063                                 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->num_textures);
2064                         else
2065                                 out->texture = loadmodel->data_textures + miptex;
2066                 }
2067                 if (out->flags & TEX_SPECIAL)
2068                 {
2069                         // if texture chosen is NULL or the shader needs a lightmap,
2070                         // force to notexture water shader
2071                         if (out->texture == NULL)
2072                                 out->texture = loadmodel->data_textures + (loadmodel->num_textures - 1);
2073                 }
2074                 else
2075                 {
2076                         // if texture chosen is NULL, force to notexture
2077                         if (out->texture == NULL)
2078                                 out->texture = loadmodel->data_textures + (loadmodel->num_textures - 2);
2079                 }
2080         }
2081 }
2082
2083 #if 0
2084 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
2085 {
2086         int             i, j;
2087         float   *v;
2088
2089         mins[0] = mins[1] = mins[2] = 9999;
2090         maxs[0] = maxs[1] = maxs[2] = -9999;
2091         v = verts;
2092         for (i = 0;i < numverts;i++)
2093         {
2094                 for (j = 0;j < 3;j++, v++)
2095                 {
2096                         if (*v < mins[j])
2097                                 mins[j] = *v;
2098                         if (*v > maxs[j])
2099                                 maxs[j] = *v;
2100                 }
2101         }
2102 }
2103
2104 #define MAX_SUBDIVPOLYTRIANGLES 4096
2105 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
2106
2107 static int subdivpolyverts, subdivpolytriangles;
2108 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
2109 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
2110
2111 static int subdivpolylookupvert(vec3_t v)
2112 {
2113         int i;
2114         for (i = 0;i < subdivpolyverts;i++)
2115                 if (subdivpolyvert[i][0] == v[0]
2116                  && subdivpolyvert[i][1] == v[1]
2117                  && subdivpolyvert[i][2] == v[2])
2118                         return i;
2119         if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
2120                 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
2121         VectorCopy(v, subdivpolyvert[subdivpolyverts]);
2122         return subdivpolyverts++;
2123 }
2124
2125 static void SubdividePolygon(int numverts, float *verts)
2126 {
2127         int             i, i1, i2, i3, f, b, c, p;
2128         vec3_t  mins, maxs, front[256], back[256];
2129         float   m, *pv, *cv, dist[256], frac;
2130
2131         if (numverts > 250)
2132                 Host_Error("SubdividePolygon: ran out of verts in buffer");
2133
2134         BoundPoly(numverts, verts, mins, maxs);
2135
2136         for (i = 0;i < 3;i++)
2137         {
2138                 m = (mins[i] + maxs[i]) * 0.5;
2139                 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
2140                 if (maxs[i] - m < 8)
2141                         continue;
2142                 if (m - mins[i] < 8)
2143                         continue;
2144
2145                 // cut it
2146                 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
2147                         dist[c] = cv[i] - m;
2148
2149                 f = b = 0;
2150                 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
2151                 {
2152                         if (dist[p] >= 0)
2153                         {
2154                                 VectorCopy(pv, front[f]);
2155                                 f++;
2156                         }
2157                         if (dist[p] <= 0)
2158                         {
2159                                 VectorCopy(pv, back[b]);
2160                                 b++;
2161                         }
2162                         if (dist[p] == 0 || dist[c] == 0)
2163                                 continue;
2164                         if ((dist[p] > 0) != (dist[c] > 0) )
2165                         {
2166                                 // clip point
2167                                 frac = dist[p] / (dist[p] - dist[c]);
2168                                 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
2169                                 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
2170                                 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
2171                                 f++;
2172                                 b++;
2173                         }
2174                 }
2175
2176                 SubdividePolygon(f, front[0]);
2177                 SubdividePolygon(b, back[0]);
2178                 return;
2179         }
2180
2181         i1 = subdivpolylookupvert(verts);
2182         i2 = subdivpolylookupvert(verts + 3);
2183         for (i = 2;i < numverts;i++)
2184         {
2185                 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
2186                 {
2187                         Con_Print("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
2188                         return;
2189                 }
2190
2191                 i3 = subdivpolylookupvert(verts + i * 3);
2192                 subdivpolyindex[subdivpolytriangles][0] = i1;
2193                 subdivpolyindex[subdivpolytriangles][1] = i2;
2194                 subdivpolyindex[subdivpolytriangles][2] = i3;
2195                 i2 = i3;
2196                 subdivpolytriangles++;
2197         }
2198 }
2199
2200 //Breaks a polygon up along axial 64 unit
2201 //boundaries so that turbulent and sky warps
2202 //can be done reasonably.
2203 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surface)
2204 {
2205         int i, j;
2206         surfvertex_t *v;
2207         surfmesh_t *mesh;
2208
2209         subdivpolytriangles = 0;
2210         subdivpolyverts = 0;
2211         SubdividePolygon(surface->num_vertices, (surface->mesh->data_vertex3f + 3 * surface->num_firstvertex));
2212         if (subdivpolytriangles < 1)
2213                 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?");
2214
2215         surface->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
2216         mesh->num_vertices = subdivpolyverts;
2217         mesh->num_triangles = subdivpolytriangles;
2218         mesh->vertex = (surfvertex_t *)(mesh + 1);
2219         mesh->index = (int *)(mesh->vertex + mesh->num_vertices);
2220         memset(mesh->vertex, 0, mesh->num_vertices * sizeof(surfvertex_t));
2221
2222         for (i = 0;i < mesh->num_triangles;i++)
2223                 for (j = 0;j < 3;j++)
2224                         mesh->index[i*3+j] = subdivpolyindex[i][j];
2225
2226         for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
2227         {
2228                 VectorCopy(subdivpolyvert[i], v->v);
2229                 v->st[0] = DotProduct(v->v, surface->lightmapinfo->texinfo->vecs[0]);
2230                 v->st[1] = DotProduct(v->v, surface->lightmapinfo->texinfo->vecs[1]);
2231         }
2232 }
2233 #endif
2234
2235 extern cvar_t gl_max_lightmapsize;
2236 static void Mod_Q1BSP_LoadFaces(lump_t *l)
2237 {
2238         dface_t *in;
2239         msurface_t *surface;
2240         int i, j, count, surfacenum, planenum, smax, tmax, ssize, tsize, firstedge, numedges, totalverts, totaltris, lightmapnumber, lightmapsize, totallightmapsamples;
2241         float texmins[2], texmaxs[2], val;
2242         rtexture_t *lightmaptexture, *deluxemaptexture;
2243
2244         in = (dface_t *)(mod_base + l->fileofs);
2245         if (l->filelen % sizeof(*in))
2246                 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
2247         count = l->filelen / sizeof(*in);
2248         loadmodel->data_surfaces = (msurface_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
2249         loadmodel->data_surfaces_lightmapinfo = (msurface_lightmapinfo_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_lightmapinfo_t));
2250
2251         loadmodel->num_surfaces = count;
2252
2253         loadmodel->brushq1.firstrender = true;
2254         loadmodel->brushq1.lightmapupdateflags = (unsigned char *)Mem_Alloc(loadmodel->mempool, count*sizeof(unsigned char));
2255
2256         totalverts = 0;
2257         totaltris = 0;
2258         for (surfacenum = 0, in = (dface_t *)(mod_base + l->fileofs);surfacenum < count;surfacenum++, in++)
2259         {
2260                 numedges = (unsigned short)LittleShort(in->numedges);
2261                 totalverts += numedges;
2262                 totaltris += numedges - 2;
2263         }
2264
2265         Mod_AllocSurfMesh(loadmodel->mempool, totalverts, totaltris, true, false, false);
2266
2267         lightmaptexture = NULL;
2268         deluxemaptexture = r_texture_blanknormalmap;
2269         lightmapnumber = 0;
2270         lightmapsize = bound(256, gl_max_lightmapsize.integer, (int)vid.maxtexturesize_2d);
2271         totallightmapsamples = 0;
2272
2273         totalverts = 0;
2274         totaltris = 0;
2275         for (surfacenum = 0, in = (dface_t *)(mod_base + l->fileofs), surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, in++, surface++)
2276         {
2277                 surface->lightmapinfo = loadmodel->data_surfaces_lightmapinfo + surfacenum;
2278
2279                 // FIXME: validate edges, texinfo, etc?
2280                 firstedge = LittleLong(in->firstedge);
2281                 numedges = (unsigned short)LittleShort(in->numedges);
2282                 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)
2283                         Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)", firstedge, numedges, loadmodel->brushq1.numsurfedges);
2284                 i = (unsigned short)LittleShort(in->texinfo);
2285                 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
2286                         Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)", i, loadmodel->brushq1.numtexinfo);
2287                 surface->lightmapinfo->texinfo = loadmodel->brushq1.texinfo + i;
2288                 surface->texture = surface->lightmapinfo->texinfo->texture;
2289
2290                 planenum = (unsigned short)LittleShort(in->planenum);
2291                 if ((unsigned int) planenum >= (unsigned int) loadmodel->brush.num_planes)
2292                         Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)", planenum, loadmodel->brush.num_planes);
2293
2294                 //surface->flags = surface->texture->flags;
2295                 //if (LittleShort(in->side))
2296                 //      surface->flags |= SURF_PLANEBACK;
2297                 //surface->plane = loadmodel->brush.data_planes + planenum;
2298
2299                 surface->num_firstvertex = totalverts;
2300                 surface->num_vertices = numedges;
2301                 surface->num_firsttriangle = totaltris;
2302                 surface->num_triangles = numedges - 2;
2303                 totalverts += numedges;
2304                 totaltris += numedges - 2;
2305
2306                 // convert edges back to a normal polygon
2307                 for (i = 0;i < surface->num_vertices;i++)
2308                 {
2309                         int lindex = loadmodel->brushq1.surfedges[firstedge + i];
2310                         float s, t;
2311                         // note: the q1bsp format does not allow a 0 surfedge (it would have no negative counterpart)
2312                         if (lindex >= 0)
2313                                 VectorCopy(loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position, (loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3);
2314                         else
2315                                 VectorCopy(loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position, (loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3);
2316                         s = DotProduct(((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3), surface->lightmapinfo->texinfo->vecs[0]) + surface->lightmapinfo->texinfo->vecs[0][3];
2317                         t = DotProduct(((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3), surface->lightmapinfo->texinfo->vecs[1]) + surface->lightmapinfo->texinfo->vecs[1][3];
2318                         (loadmodel->surfmesh.data_texcoordtexture2f + 2 * surface->num_firstvertex)[i * 2 + 0] = s / surface->texture->width;
2319                         (loadmodel->surfmesh.data_texcoordtexture2f + 2 * surface->num_firstvertex)[i * 2 + 1] = t / surface->texture->height;
2320                         (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 0] = 0;
2321                         (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 1] = 0;
2322                         (loadmodel->surfmesh.data_lightmapoffsets + surface->num_firstvertex)[i] = 0;
2323                 }
2324
2325                 for (i = 0;i < surface->num_triangles;i++)
2326                 {
2327                         (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle)[i * 3 + 0] = 0 + surface->num_firstvertex;
2328                         (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle)[i * 3 + 1] = i + 1 + surface->num_firstvertex;
2329                         (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle)[i * 3 + 2] = i + 2 + surface->num_firstvertex;
2330                 }
2331
2332                 // compile additional data about the surface geometry
2333                 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);
2334                 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);
2335                 BoxFromPoints(surface->mins, surface->maxs, surface->num_vertices, (loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex));
2336
2337                 // generate surface extents information
2338                 texmins[0] = texmaxs[0] = DotProduct((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex), surface->lightmapinfo->texinfo->vecs[0]) + surface->lightmapinfo->texinfo->vecs[0][3];
2339                 texmins[1] = texmaxs[1] = DotProduct((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex), surface->lightmapinfo->texinfo->vecs[1]) + surface->lightmapinfo->texinfo->vecs[1][3];
2340                 for (i = 1;i < surface->num_vertices;i++)
2341                 {
2342                         for (j = 0;j < 2;j++)
2343                         {
2344                                 val = DotProduct((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3, surface->lightmapinfo->texinfo->vecs[j]) + surface->lightmapinfo->texinfo->vecs[j][3];
2345                                 texmins[j] = min(texmins[j], val);
2346                                 texmaxs[j] = max(texmaxs[j], val);
2347                         }
2348                 }
2349                 for (i = 0;i < 2;i++)
2350                 {
2351                         surface->lightmapinfo->texturemins[i] = (int) floor(texmins[i] / 16.0) * 16;
2352                         surface->lightmapinfo->extents[i] = (int) ceil(texmaxs[i] / 16.0) * 16 - surface->lightmapinfo->texturemins[i];
2353                 }
2354
2355                 smax = surface->lightmapinfo->extents[0] >> 4;
2356                 tmax = surface->lightmapinfo->extents[1] >> 4;
2357                 ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
2358                 tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
2359
2360                 // lighting info
2361                 for (i = 0;i < MAXLIGHTMAPS;i++)
2362                         surface->lightmapinfo->styles[i] = in->styles[i];
2363                 surface->lightmaptexture = NULL;
2364                 surface->deluxemaptexture = r_texture_blanknormalmap;
2365                 i = LittleLong(in->lightofs);
2366                 if (i == -1)
2367                 {
2368                         surface->lightmapinfo->samples = NULL;
2369 #if 1
2370                         // give non-lightmapped water a 1x white lightmap
2371                         if (surface->texture->name[0] == '*' && (surface->lightmapinfo->texinfo->flags & TEX_SPECIAL) && ssize <= 256 && tsize <= 256)
2372                         {
2373                                 surface->lightmapinfo->samples = (unsigned char *)Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
2374                                 surface->lightmapinfo->styles[0] = 0;
2375                                 memset(surface->lightmapinfo->samples, 128, ssize * tsize * 3);
2376                         }
2377 #endif
2378                 }
2379                 else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
2380                         surface->lightmapinfo->samples = loadmodel->brushq1.lightdata + i;
2381                 else // LordHavoc: white lighting (bsp version 29)
2382                 {
2383                         surface->lightmapinfo->samples = loadmodel->brushq1.lightdata + (i * 3);
2384                         if (loadmodel->brushq1.nmaplightdata)
2385                                 surface->lightmapinfo->nmapsamples = loadmodel->brushq1.nmaplightdata + (i * 3);
2386                 }
2387
2388                 // check if we should apply a lightmap to this
2389                 if (!(surface->lightmapinfo->texinfo->flags & TEX_SPECIAL) || surface->lightmapinfo->samples)
2390                 {
2391                         if (ssize > 256 || tsize > 256)
2392                                 Host_Error("Bad surface extents");
2393
2394                         if (lightmapsize < ssize)
2395                                 lightmapsize = ssize;
2396                         if (lightmapsize < tsize)
2397                                 lightmapsize = tsize;
2398
2399                         totallightmapsamples += ssize*tsize;
2400
2401                         // force lightmap upload on first time seeing the surface
2402                         //
2403                         // additionally this is used by the later code to see if a
2404                         // lightmap is needed on this surface (rather than duplicating the
2405                         // logic above)
2406                         loadmodel->brushq1.lightmapupdateflags[surfacenum] = true;
2407                 }
2408         }
2409
2410         // small maps (such as ammo boxes especially) don't need big lightmap
2411         // textures, so this code tries to guess a good size based on
2412         // totallightmapsamples (size of the lightmaps lump basically), as well as
2413         // trying to max out the size if there is a lot of lightmap data to store
2414         // additionally, never choose a lightmapsize that is smaller than the
2415         // largest surface encountered (as it would fail)
2416         i = lightmapsize;
2417         for (lightmapsize = 64; (lightmapsize < i) && (lightmapsize < bound(128, gl_max_lightmapsize.integer, (int)vid.maxtexturesize_2d)) && (totallightmapsamples > lightmapsize*lightmapsize); lightmapsize*=2)
2418                 ;
2419
2420         // now that we've decided the lightmap texture size, we can do the rest
2421         if (cls.state != ca_dedicated)
2422         {
2423                 int stainmapsize = 0;
2424                 mod_alloclightmap_state_t allocState;
2425
2426                 Mod_AllocLightmap_Init(&allocState, lightmapsize, lightmapsize);
2427                 for (surfacenum = 0, surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, surface++)
2428                 {
2429                         int i, iu, iv, lightmapx = 0, lightmapy = 0;
2430                         float u, v, ubase, vbase, uscale, vscale;
2431
2432                         if (!loadmodel->brushq1.lightmapupdateflags[surfacenum])
2433                                 continue;
2434
2435                         smax = surface->lightmapinfo->extents[0] >> 4;
2436                         tmax = surface->lightmapinfo->extents[1] >> 4;
2437                         ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
2438                         tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
2439                         stainmapsize += ssize * tsize * 3;
2440
2441                         if (!lightmaptexture || !Mod_AllocLightmap_Block(&allocState, ssize, tsize, &lightmapx, &lightmapy))
2442                         {
2443                                 // allocate a texture pool if we need it
2444                                 if (loadmodel->texturepool == NULL)
2445                                         loadmodel->texturepool = R_AllocTexturePool();
2446                                 // could not find room, make a new lightmap
2447                                 loadmodel->brushq3.num_mergedlightmaps = lightmapnumber + 1;
2448                                 loadmodel->brushq3.data_lightmaps = Mem_Realloc(loadmodel->mempool, loadmodel->brushq3.data_lightmaps, loadmodel->brushq3.num_mergedlightmaps * sizeof(loadmodel->brushq3.data_lightmaps[0]));
2449                                 loadmodel->brushq3.data_deluxemaps = Mem_Realloc(loadmodel->mempool, loadmodel->brushq3.data_deluxemaps, loadmodel->brushq3.num_mergedlightmaps * sizeof(loadmodel->brushq3.data_deluxemaps[0]));
2450                                 loadmodel->brushq3.data_lightmaps[lightmapnumber] = lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%i", lightmapnumber), lightmapsize, lightmapsize, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_ALLOWUPDATES, NULL);
2451                                 if (loadmodel->brushq1.nmaplightdata)
2452                                         loadmodel->brushq3.data_deluxemaps[lightmapnumber] = deluxemaptexture = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%i", lightmapnumber), lightmapsize, lightmapsize, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_ALLOWUPDATES, NULL);
2453                                 lightmapnumber++;
2454                                 Mod_AllocLightmap_Reset(&allocState);
2455                                 Mod_AllocLightmap_Block(&allocState, ssize, tsize, &lightmapx, &lightmapy);
2456                         }
2457                         surface->lightmaptexture = lightmaptexture;
2458                         surface->deluxemaptexture = deluxemaptexture;
2459                         surface->lightmapinfo->lightmaporigin[0] = lightmapx;
2460                         surface->lightmapinfo->lightmaporigin[1] = lightmapy;
2461
2462                         uscale = 1.0f / (float)lightmapsize;
2463                         vscale = 1.0f / (float)lightmapsize;
2464                         ubase = lightmapx * uscale;
2465                         vbase = lightmapy * vscale;
2466
2467                         for (i = 0;i < surface->num_vertices;i++)
2468                         {
2469                                 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);
2470                                 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);
2471                                 (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 0] = u * uscale + ubase;
2472                                 (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 1] = v * vscale + vbase;
2473                                 // LordHavoc: calc lightmap data offset for vertex lighting to use
2474                                 iu = (int) u;
2475                                 iv = (int) v;
2476                                 (loadmodel->surfmesh.data_lightmapoffsets + surface->num_firstvertex)[i] = (bound(0, iv, tmax) * ssize + bound(0, iu, smax)) * 3;
2477                         }
2478                 }
2479
2480                 if (cl_stainmaps.integer)
2481                 {
2482                         // allocate stainmaps for permanent marks on walls and clear white
2483                         unsigned char *stainsamples = NULL;
2484                         stainsamples = (unsigned char *)Mem_Alloc(loadmodel->mempool, stainmapsize);
2485                         memset(stainsamples, 255, stainmapsize);
2486                         // assign pointers
2487                         for (surfacenum = 0, surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, surface++)
2488                         {
2489                                 if (!loadmodel->brushq1.lightmapupdateflags[surfacenum])
2490                                         continue;
2491                                 ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
2492                                 tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
2493                                 surface->lightmapinfo->stainsamples = stainsamples;
2494                                 stainsamples += ssize * tsize * 3;
2495                         }
2496                 }
2497         }
2498
2499         // generate ushort elements array if possible
2500         if (loadmodel->surfmesh.data_element3s)
2501                 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
2502                         loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
2503 }
2504
2505 static void Mod_Q1BSP_LoadNodes_RecursiveSetParent(mnode_t *node, mnode_t *parent)
2506 {
2507         //if (node->parent)
2508         //      Host_Error("Mod_Q1BSP_LoadNodes_RecursiveSetParent: runaway recursion");
2509         node->parent = parent;
2510         if (node->plane)
2511         {
2512                 // this is a node, recurse to children
2513                 Mod_Q1BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
2514                 Mod_Q1BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
2515                 // combine supercontents of children
2516                 node->combinedsupercontents = node->children[0]->combinedsupercontents | node->children[1]->combinedsupercontents;
2517         }
2518         else
2519         {
2520                 int j;
2521                 mleaf_t *leaf = (mleaf_t *)node;
2522                 // if this is a leaf, calculate supercontents mask from all collidable
2523                 // primitives in the leaf (brushes and collision surfaces)
2524                 // also flag if the leaf contains any collision surfaces
2525                 leaf->combinedsupercontents = 0;
2526                 // combine the supercontents values of all brushes in this leaf
2527                 for (j = 0;j < leaf->numleafbrushes;j++)
2528                         leaf->combinedsupercontents |= loadmodel->brush.data_brushes[leaf->firstleafbrush[j]].texture->supercontents;
2529                 // check if this leaf contains any collision surfaces (q3 patches)
2530                 for (j = 0;j < leaf->numleafsurfaces;j++)
2531                 {
2532                         msurface_t *surface = loadmodel->data_surfaces + leaf->firstleafsurface[j];
2533                         if (surface->num_collisiontriangles)
2534                         {
2535                                 leaf->containscollisionsurfaces = true;
2536                                 leaf->combinedsupercontents |= surface->texture->supercontents;
2537                         }
2538                 }
2539         }
2540 }
2541
2542 static void Mod_Q1BSP_LoadNodes(lump_t *l)
2543 {
2544         int                     i, j, count, p;
2545         dnode_t         *in;
2546         mnode_t         *out;
2547
2548         in = (dnode_t *)(mod_base + l->fileofs);
2549         if (l->filelen % sizeof(*in))
2550                 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
2551         count = l->filelen / sizeof(*in);
2552         out = (mnode_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2553
2554         loadmodel->brush.data_nodes = out;
2555         loadmodel->brush.num_nodes = count;
2556
2557         for ( i=0 ; i<count ; i++, in++, out++)
2558         {
2559                 for (j=0 ; j<3 ; j++)
2560                 {
2561                         out->mins[j] = LittleShort(in->mins[j]);
2562                         out->maxs[j] = LittleShort(in->maxs[j]);
2563                 }
2564
2565                 p = LittleLong(in->planenum);
2566                 out->plane = loadmodel->brush.data_planes + p;
2567
2568                 out->firstsurface = (unsigned short)LittleShort(in->firstface);
2569                 out->numsurfaces = (unsigned short)LittleShort(in->numfaces);
2570
2571                 for (j=0 ; j<2 ; j++)
2572                 {
2573                         // LordHavoc: this code supports broken bsp files produced by
2574                         // arguire qbsp which can produce more than 32768 nodes, any value
2575                         // below count is assumed to be a node number, any other value is
2576                         // assumed to be a leaf number
2577                         p = (unsigned short)LittleShort(in->children[j]);
2578                         if (p < count)
2579                         {
2580                                 if (p < loadmodel->brush.num_nodes)
2581                                         out->children[j] = loadmodel->brush.data_nodes + p;
2582                                 else
2583                                 {
2584                                         Con_Printf("Mod_Q1BSP_LoadNodes: invalid node index %i (file has only %i nodes)\n", p, loadmodel->brush.num_nodes);
2585                                         // map it to the solid leaf
2586                                         out->children[j] = (mnode_t *)loadmodel->brush.data_leafs;
2587                                 }
2588                         }
2589                         else
2590                         {
2591                                 // note this uses 65535 intentionally, -1 is leaf 0
2592                                 p = 65535 - p;
2593                                 if (p < loadmodel->brush.num_leafs)
2594                                         out->children[j] = (mnode_t *)(loadmodel->brush.data_leafs + p);
2595                                 else
2596                                 {
2597                                         Con_Printf("Mod_Q1BSP_LoadNodes: invalid leaf index %i (file has only %i leafs)\n", p, loadmodel->brush.num_leafs);
2598                                         // map it to the solid leaf
2599                                         out->children[j] = (mnode_t *)loadmodel->brush.data_leafs;
2600                                 }
2601                         }
2602                 }
2603         }
2604
2605         Mod_Q1BSP_LoadNodes_RecursiveSetParent(loadmodel->brush.data_nodes, NULL);      // sets nodes and leafs
2606 }
2607
2608 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
2609 {
2610         dleaf_t *in;
2611         mleaf_t *out;
2612         int i, j, count, p;
2613
2614         in = (dleaf_t *)(mod_base + l->fileofs);
2615         if (l->filelen % sizeof(*in))
2616                 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
2617         count = l->filelen / sizeof(*in);
2618         out = (mleaf_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2619
2620         loadmodel->brush.data_leafs = out;
2621         loadmodel->brush.num_leafs = count;
2622         // get visleafs from the submodel data
2623         loadmodel->brush.num_pvsclusters = loadmodel->brushq1.submodels[0].visleafs;
2624         loadmodel->brush.num_pvsclusterbytes = (loadmodel->brush.num_pvsclusters+7)>>3;
2625         loadmodel->brush.data_pvsclusters = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_pvsclusters * loadmodel->brush.num_pvsclusterbytes);
2626         memset(loadmodel->brush.data_pvsclusters, 0xFF, loadmodel->brush.num_pvsclusters * loadmodel->brush.num_pvsclusterbytes);
2627
2628         for ( i=0 ; i<count ; i++, in++, out++)
2629         {
2630                 for (j=0 ; j<3 ; j++)
2631                 {
2632                         out->mins[j] = LittleShort(in->mins[j]);
2633                         out->maxs[j] = LittleShort(in->maxs[j]);
2634                 }
2635
2636                 // FIXME: this function could really benefit from some error checking
2637
2638                 out->contents = LittleLong(in->contents);
2639
2640                 out->firstleafsurface = loadmodel->brush.data_leafsurfaces + (unsigned short)LittleShort(in->firstmarksurface);
2641                 out->numleafsurfaces = (unsigned short)LittleShort(in->nummarksurfaces);
2642                 if ((unsigned short)LittleShort(in->firstmarksurface) + out->numleafsurfaces > loadmodel->brush.num_leafsurfaces)
2643                 {
2644                         Con_Printf("Mod_Q1BSP_LoadLeafs: invalid leafsurface range %i:%i outside range %i:%i\n", (int)(out->firstleafsurface - loadmodel->brush.data_leafsurfaces), (int)(out->firstleafsurface + out->numleafsurfaces - loadmodel->brush.data_leafsurfaces), 0, loadmodel->brush.num_leafsurfaces);
2645                         out->firstleafsurface = NULL;
2646                         out->numleafsurfaces = 0;
2647                 }
2648
2649                 out->clusterindex = i - 1;
2650                 if (out->clusterindex >= loadmodel->brush.num_pvsclusters)
2651                         out->clusterindex = -1;
2652
2653                 p = LittleLong(in->visofs);
2654                 // ignore visofs errors on leaf 0 (solid)
2655                 if (p >= 0 && out->clusterindex >= 0)
2656                 {
2657                         if (p >= loadmodel->brushq1.num_compressedpvs)
2658                                 Con_Print("Mod_Q1BSP_LoadLeafs: invalid visofs\n");
2659                         else
2660                                 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);
2661                 }
2662
2663                 for (j = 0;j < 4;j++)
2664                         out->ambient_sound_level[j] = in->ambient_level[j];
2665
2666                 // FIXME: Insert caustics here
2667         }
2668 }
2669
2670 qboolean Mod_Q1BSP_CheckWaterAlphaSupport(void)
2671 {
2672         int i, j;
2673         mleaf_t *leaf;
2674         const unsigned char *pvs;
2675         // if there's no vis data, assume supported (because everything is visible all the time)
2676         if (!loadmodel->brush.data_pvsclusters)
2677                 return true;
2678         // check all liquid leafs to see if they can see into empty leafs, if any
2679         // can we can assume this map supports r_wateralpha
2680         for (i = 0, leaf = loadmodel->brush.data_leafs;i < loadmodel->brush.num_leafs;i++, leaf++)
2681         {
2682                 if ((leaf->contents == CONTENTS_WATER || leaf->contents == CONTENTS_SLIME) && leaf->clusterindex >= 0)
2683                 {
2684                         pvs = loadmodel->brush.data_pvsclusters + leaf->clusterindex * loadmodel->brush.num_pvsclusterbytes;
2685                         for (j = 0;j < loadmodel->brush.num_leafs;j++)
2686                                 if (CHECKPVSBIT(pvs, loadmodel->brush.data_leafs[j].clusterindex) && loadmodel->brush.data_leafs[j].contents == CONTENTS_EMPTY)
2687                                         return true;
2688                 }
2689         }
2690         return false;
2691 }
2692
2693 static void Mod_Q1BSP_LoadClipnodes(lump_t *l, hullinfo_t *hullinfo)
2694 {
2695         dclipnode_t *in;
2696         mclipnode_t *out;
2697         int                     i, count;
2698         hull_t          *hull;
2699
2700         in = (dclipnode_t *)(mod_base + l->fileofs);
2701         if (l->filelen % sizeof(*in))
2702                 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
2703         count = l->filelen / sizeof(*in);
2704         out = (mclipnode_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2705
2706         loadmodel->brushq1.clipnodes = out;
2707         loadmodel->brushq1.numclipnodes = count;
2708
2709         for (i = 1; i < MAX_MAP_HULLS; i++)
2710         {
2711                 hull = &loadmodel->brushq1.hulls[i];
2712                 hull->clipnodes = out;
2713                 hull->firstclipnode = 0;
2714                 hull->lastclipnode = count-1;
2715                 hull->planes = loadmodel->brush.data_planes;
2716                 hull->clip_mins[0] = hullinfo->hullsizes[i][0][0];
2717                 hull->clip_mins[1] = hullinfo->hullsizes[i][0][1];
2718                 hull->clip_mins[2] = hullinfo->hullsizes[i][0][2];
2719                 hull->clip_maxs[0] = hullinfo->hullsizes[i][1][0];
2720                 hull->clip_maxs[1] = hullinfo->hullsizes[i][1][1];
2721                 hull->clip_maxs[2] = hullinfo->hullsizes[i][1][2];
2722                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2723         }
2724
2725         for (i=0 ; i<count ; i++, out++, in++)
2726         {
2727                 out->planenum = LittleLong(in->planenum);
2728                 // LordHavoc: this code supports arguire qbsp's broken clipnodes indices (more than 32768 clipnodes), values above count are assumed to be contents values
2729                 out->children[0] = (unsigned short)LittleShort(in->children[0]);
2730                 out->children[1] = (unsigned short)LittleShort(in->children[1]);
2731                 if (out->children[0] >= count)
2732                         out->children[0] -= 65536;
2733                 if (out->children[1] >= count)
2734                         out->children[1] -= 65536;
2735                 if (out->planenum < 0 || out->planenum >= loadmodel->brush.num_planes)
2736                         Host_Error("Corrupt clipping hull(out of range planenum)");
2737         }
2738 }
2739
2740 //Duplicate the drawing hull structure as a clipping hull
2741 static void Mod_Q1BSP_MakeHull0(void)
2742 {
2743         mnode_t         *in;
2744         mclipnode_t *out;
2745         int                     i;
2746         hull_t          *hull;
2747
2748         hull = &loadmodel->brushq1.hulls[0];
2749
2750         in = loadmodel->brush.data_nodes;
2751         out = (mclipnode_t *)Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_nodes * sizeof(*out));
2752
2753         hull->clipnodes = out;
2754         hull->firstclipnode = 0;
2755         hull->lastclipnode = loadmodel->brush.num_nodes - 1;
2756         hull->planes = loadmodel->brush.data_planes;
2757
2758         for (i = 0;i < loadmodel->brush.num_nodes;i++, out++, in++)
2759         {
2760                 out->planenum = in->plane - loadmodel->brush.data_planes;
2761                 out->children[0] = in->children[0]->plane ? in->children[0] - loadmodel->brush.data_nodes : ((mleaf_t *)in->children[0])->contents;
2762                 out->children[1] = in->children[1]->plane ? in->children[1] - loadmodel->brush.data_nodes : ((mleaf_t *)in->children[1])->contents;
2763         }
2764 }
2765
2766 static void Mod_Q1BSP_LoadLeaffaces(lump_t *l)
2767 {
2768         int i, j;
2769         short *in;
2770
2771         in = (short *)(mod_base + l->fileofs);
2772         if (l->filelen % sizeof(*in))
2773                 Host_Error("Mod_Q1BSP_LoadLeaffaces: funny lump size in %s",loadmodel->name);
2774         loadmodel->brush.num_leafsurfaces = l->filelen / sizeof(*in);
2775         loadmodel->brush.data_leafsurfaces = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_leafsurfaces * sizeof(int));
2776
2777         for (i = 0;i < loadmodel->brush.num_leafsurfaces;i++)
2778         {
2779                 j = (unsigned short) LittleShort(in[i]);
2780                 if (j >= loadmodel->num_surfaces)
2781                         Host_Error("Mod_Q1BSP_LoadLeaffaces: bad surface number");
2782                 loadmodel->brush.data_leafsurfaces[i] = j;
2783         }
2784 }
2785
2786 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
2787 {
2788         int             i;
2789         int             *in;
2790
2791         in = (int *)(mod_base + l->fileofs);
2792         if (l->filelen % sizeof(*in))
2793                 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
2794         loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
2795         loadmodel->brushq1.surfedges = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
2796
2797         for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
2798                 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
2799 }
2800
2801
2802 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
2803 {
2804         int                     i;
2805         mplane_t        *out;
2806         dplane_t        *in;
2807
2808         in = (dplane_t *)(mod_base + l->fileofs);
2809         if (l->filelen % sizeof(*in))
2810                 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
2811
2812         loadmodel->brush.num_planes = l->filelen / sizeof(*in);
2813         loadmodel->brush.data_planes = out = (mplane_t *)Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_planes * sizeof(*out));
2814
2815         for (i = 0;i < loadmodel->brush.num_planes;i++, in++, out++)
2816         {
2817                 out->normal[0] = LittleFloat(in->normal[0]);
2818                 out->normal[1] = LittleFloat(in->normal[1]);
2819                 out->normal[2] = LittleFloat(in->normal[2]);
2820                 out->dist = LittleFloat(in->dist);
2821
2822                 PlaneClassify(out);
2823         }
2824 }
2825
2826 static void Mod_Q1BSP_LoadMapBrushes(void)
2827 {
2828 #if 0
2829 // unfinished
2830         int submodel, numbrushes;
2831         qboolean firstbrush;
2832         char *text, *maptext;
2833         char mapfilename[MAX_QPATH];
2834         FS_StripExtension (loadmodel->name, mapfilename, sizeof (mapfilename));
2835         strlcat (mapfilename, ".map", sizeof (mapfilename));
2836         maptext = (unsigned char*) FS_LoadFile(mapfilename, tempmempool, false, NULL);
2837         if (!maptext)
2838                 return;
2839         text = maptext;
2840         if (!COM_ParseToken_Simple(&data, false, false))
2841                 return; // error
2842         submodel = 0;
2843         for (;;)
2844         {
2845                 if (!COM_ParseToken_Simple(&data, false, false))
2846                         break;
2847                 if (com_token[0] != '{')
2848                         return; // error
2849                 // entity
2850                 firstbrush = true;
2851                 numbrushes = 0;
2852                 maxbrushes = 256;
2853                 brushes = Mem_Alloc(loadmodel->mempool, maxbrushes * sizeof(mbrush_t));
2854                 for (;;)
2855                 {
2856                         if (!COM_ParseToken_Simple(&data, false, false))
2857                                 return; // error
2858                         if (com_token[0] == '}')
2859                                 break; // end of entity
2860                         if (com_token[0] == '{')
2861                         {
2862                                 // brush
2863                                 if (firstbrush)
2864                                 {
2865                                         if (submodel)
2866                                         {
2867                                                 if (submodel > loadmodel->brush.numsubmodels)
2868                                                 {
2869                                                         Con_Printf("Mod_Q1BSP_LoadMapBrushes: .map has more submodels than .bsp!\n");
2870                                                         model = NULL;
2871                                                 }
2872                                                 else
2873                                                         model = loadmodel->brush.submodels[submodel];
2874                                         }
2875                                         else
2876                                                 model = loadmodel;
2877                                 }
2878                                 for (;;)
2879                                 {
2880                                         if (!COM_ParseToken_Simple(&data, false, false))
2881                                                 return; // error
2882                                         if (com_token[0] == '}')
2883                                                 break; // end of brush
2884                                         // each brush face should be this format:
2885                                         // ( x y z ) ( x y z ) ( x y z ) texture scroll_s scroll_t rotateangle scale_s scale_t
2886                                         // FIXME: support hl .map format
2887                                         for (pointnum = 0;pointnum < 3;pointnum++)
2888                                         {
2889                                                 COM_ParseToken_Simple(&data, false, false);
2890                                                 for (componentnum = 0;componentnum < 3;componentnum++)
2891                                                 {
2892                                                         COM_ParseToken_Simple(&data, false, false);
2893                                                         point[pointnum][componentnum] = atof(com_token);
2894                                                 }
2895                                                 COM_ParseToken_Simple(&data, false, false);
2896                                         }
2897                                         COM_ParseToken_Simple(&data, false, false);
2898                                         strlcpy(facetexture, com_token, sizeof(facetexture));
2899                                         COM_ParseToken_Simple(&data, false, false);
2900                                         //scroll_s = atof(com_token);
2901                                         COM_ParseToken_Simple(&data, false, false);
2902                                         //scroll_t = atof(com_token);
2903                                         COM_ParseToken_Simple(&data, false, false);
2904                                         //rotate = atof(com_token);
2905                                         COM_ParseToken_Simple(&data, false, false);
2906                                         //scale_s = atof(com_token);
2907                                         COM_ParseToken_Simple(&data, false, false);
2908                                         //scale_t = atof(com_token);
2909                                         TriangleNormal(point[0], point[1], point[2], planenormal);
2910                                         VectorNormalizeDouble(planenormal);
2911                                         planedist = DotProduct(point[0], planenormal);
2912                                         //ChooseTexturePlane(planenormal, texturevector[0], texturevector[1]);
2913                                 }
2914                                 continue;
2915                         }
2916                 }
2917         }
2918 #endif
2919 }
2920
2921
2922 #define MAX_PORTALPOINTS 64
2923
2924 typedef struct portal_s
2925 {
2926         mplane_t plane;
2927         mnode_t *nodes[2];              // [0] = front side of plane
2928         struct portal_s *next[2];
2929         int numpoints;
2930         double points[3*MAX_PORTALPOINTS];
2931         struct portal_s *chain; // all portals are linked into a list
2932 }
2933 portal_t;
2934
2935 static memexpandablearray_t portalarray;
2936
2937 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2938 {
2939         // process only nodes (leafs already had their box calculated)
2940         if (!node->plane)
2941                 return;
2942
2943         // calculate children first
2944         Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2945         Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2946
2947         // make combined bounding box from children
2948         node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2949         node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2950         node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2951         node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2952         node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2953         node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2954 }
2955
2956 static void Mod_Q1BSP_FinalizePortals(void)
2957 {
2958         int i, j, numportals, numpoints, portalindex, portalrange = Mem_ExpandableArray_IndexRange(&portalarray);
2959         portal_t *p;
2960         mportal_t *portal;
2961         mvertex_t *point;
2962         mleaf_t *leaf, *endleaf;
2963
2964         // tally up portal and point counts and recalculate bounding boxes for all
2965         // leafs (because qbsp is very sloppy)
2966         leaf = loadmodel->brush.data_leafs;
2967         endleaf = leaf + loadmodel->brush.num_leafs;
2968         for (;leaf < endleaf;leaf++)
2969         {
2970                 VectorSet(leaf->mins,  2000000000,  2000000000,  2000000000);
2971                 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2972         }
2973         numportals = 0;
2974         numpoints = 0;
2975         for (portalindex = 0;portalindex < portalrange;portalindex++)
2976         {
2977                 p = (portal_t*)Mem_ExpandableArray_RecordAtIndex(&portalarray, portalindex);
2978                 if (!p)
2979                         continue;
2980                 // note: this check must match the one below or it will usually corrupt memory
2981                 // 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
2982                 if (p->numpoints >= 3 && p->nodes[0] != p->nodes[1] && ((mleaf_t *)p->nodes[0])->clusterindex >= 0 && ((mleaf_t *)p->nodes[1])->clusterindex >= 0)
2983                 {
2984                         numportals += 2;
2985                         numpoints += p->numpoints * 2;
2986                 }
2987         }
2988         loadmodel->brush.data_portals = (mportal_t *)Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2989         loadmodel->brush.num_portals = numportals;
2990         loadmodel->brush.data_portalpoints = (mvertex_t *)((unsigned char *) loadmodel->brush.data_portals + numportals * sizeof(mportal_t));
2991         loadmodel->brush.num_portalpoints = numpoints;
2992         // clear all leaf portal chains
2993         for (i = 0;i < loadmodel->brush.num_leafs;i++)
2994                 loadmodel->brush.data_leafs[i].portals = NULL;
2995         // process all portals in the global portal chain, while freeing them
2996         portal = loadmodel->brush.data_portals;
2997         point = loadmodel->brush.data_portalpoints;
2998         for (portalindex = 0;portalindex < portalrange;portalindex++)
2999         {
3000                 p = (portal_t*)Mem_ExpandableArray_RecordAtIndex(&portalarray, portalindex);
3001                 if (!p)
3002                         continue;
3003                 if (p->numpoints >= 3 && p->nodes[0] != p->nodes[1])
3004                 {
3005                         // note: this check must match the one above or it will usually corrupt memory
3006                         // 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
3007                         if (((mleaf_t *)p->nodes[0])->clusterindex >= 0 && ((mleaf_t *)p->nodes[1])->clusterindex >= 0)
3008                         {
3009                                 // first make the back to front portal(forward portal)
3010                                 portal->points = point;
3011                                 portal->numpoints = p->numpoints;
3012                                 portal->plane.dist = p->plane.dist;
3013                                 VectorCopy(p->plane.normal, portal->plane.normal);
3014                                 portal->here = (mleaf_t *)p->nodes[1];
3015                                 portal->past = (mleaf_t *)p->nodes[0];
3016                                 // copy points
3017                                 for (j = 0;j < portal->numpoints;j++)
3018                                 {
3019                                         VectorCopy(p->points + j*3, point->position);
3020                                         point++;
3021                                 }
3022                                 BoxFromPoints(portal->mins, portal->maxs, portal->numpoints, portal->points->position);
3023                                 PlaneClassify(&portal->plane);
3024
3025                                 // link into leaf's portal chain
3026                                 portal->next = portal->here->portals;
3027                                 portal->here->portals = portal;
3028
3029                                 // advance to next portal
3030                                 portal++;
3031
3032                                 // then make the front to back portal(backward portal)
3033                                 portal->points = point;
3034                                 portal->numpoints = p->numpoints;
3035                                 portal->plane.dist = -p->plane.dist;
3036                                 VectorNegate(p->plane.normal, portal->plane.normal);
3037                                 portal->here = (mleaf_t *)p->nodes[0];
3038                                 portal->past = (mleaf_t *)p->nodes[1];
3039                                 // copy points
3040                                 for (j = portal->numpoints - 1;j >= 0;j--)
3041                                 {
3042                                         VectorCopy(p->points + j*3, point->position);
3043                                         point++;
3044                                 }
3045                                 BoxFromPoints(portal->mins, portal->maxs, portal->numpoints, portal->points->position);
3046                                 PlaneClassify(&portal->plane);
3047
3048                                 // link into leaf's portal chain
3049                                 portal->next = portal->here->portals;
3050                                 portal->here->portals = portal;
3051
3052                                 // advance to next portal
3053                                 portal++;
3054                         }
3055                         // add the portal's polygon points to the leaf bounding boxes
3056                         for (i = 0;i < 2;i++)
3057                         {
3058                                 leaf = (mleaf_t *)p->nodes[i];
3059                                 for (j = 0;j < p->numpoints;j++)
3060                                 {
3061                                         if (leaf->mins[0] > p->points[j*3+0]) leaf->mins[0] = p->points[j*3+0];
3062                                         if (leaf->mins[1] > p->points[j*3+1]) leaf->mins[1] = p->points[j*3+1];
3063                                         if (leaf->mins[2] > p->points[j*3+2]) leaf->mins[2] = p->points[j*3+2];
3064                                         if (leaf->maxs[0] < p->points[j*3+0]) leaf->maxs[0] = p->points[j*3+0];
3065                                         if (leaf->maxs[1] < p->points[j*3+1]) leaf->maxs[1] = p->points[j*3+1];
3066                                         if (leaf->maxs[2] < p->points[j*3+2]) leaf->maxs[2] = p->points[j*3+2];
3067                                 }
3068                         }
3069                 }
3070         }
3071         // now recalculate the node bounding boxes from the leafs
3072         Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brush.data_nodes + loadmodel->brushq1.hulls[0].firstclipnode);
3073 }
3074
3075 /*
3076 =============
3077 AddPortalToNodes
3078 =============
3079 */
3080 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
3081 {
3082         if (!front)
3083                 Host_Error("AddPortalToNodes: NULL front node");
3084         if (!back)
3085                 Host_Error("AddPortalToNodes: NULL back node");
3086         if (p->nodes[0] || p->nodes[1])
3087                 Host_Error("AddPortalToNodes: already included");
3088         // 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
3089
3090         p->nodes[0] = front;
3091         p->next[0] = (portal_t *)front->portals;
3092         front->portals = (mportal_t *)p;
3093
3094         p->nodes[1] = back;
3095         p->next[1] = (portal_t *)back->portals;
3096         back->portals = (mportal_t *)p;
3097 }
3098
3099 /*
3100 =============
3101 RemovePortalFromNode
3102 =============
3103 */
3104 static void RemovePortalFromNodes(portal_t *portal)
3105 {
3106         int i;
3107         mnode_t *node;
3108         void **portalpointer;
3109         portal_t *t;
3110         for (i = 0;i < 2;i++)
3111         {
3112                 node = portal->nodes[i];
3113
3114                 portalpointer = (void **) &node->portals;
3115                 while (1)
3116                 {
3117                         t = (portal_t *)*portalpointer;
3118                         if (!t)
3119                                 Host_Error("RemovePortalFromNodes: portal not in leaf");
3120
3121                         if (t == portal)
3122                         {
3123                                 if (portal->nodes[0] == node)
3124                                 {
3125                                         *portalpointer = portal->next[0];
3126                                         portal->nodes[0] = NULL;
3127                                 }
3128                                 else if (portal->nodes[1] == node)
3129                                 {
3130                                         *portalpointer = portal->next[1];
3131                                         portal->nodes[1] = NULL;
3132                                 }
3133                                 else
3134                                         Host_Error("RemovePortalFromNodes: portal not bounding leaf");
3135                                 break;
3136                         }
3137
3138                         if (t->nodes[0] == node)
3139                                 portalpointer = (void **) &t->next[0];
3140                         else if (t->nodes[1] == node)
3141                                 portalpointer = (void **) &t->next[1];
3142                         else
3143                                 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
3144                 }
3145         }
3146 }
3147
3148 #define PORTAL_DIST_EPSILON (1.0 / 32.0)
3149 static double *portalpointsbuffer;
3150 static int portalpointsbufferoffset;
3151 static int portalpointsbuffersize;
3152 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
3153 {
3154         int i, side;
3155         mnode_t *front, *back, *other_node;
3156         mplane_t clipplane, *plane;
3157         portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
3158         int numfrontpoints, numbackpoints;
3159         double *frontpoints, *backpoints;
3160
3161         // if a leaf, we're done
3162         if (!node->plane)
3163                 return;
3164
3165         // get some space for our clipping operations to use
3166         if (portalpointsbuffersize < portalpointsbufferoffset + 6*MAX_PORTALPOINTS)
3167         {
3168                 portalpointsbuffersize = portalpointsbufferoffset * 2;
3169                 portalpointsbuffer = Mem_Realloc(loadmodel->mempool, portalpointsbuffer, portalpointsbuffersize * sizeof(*portalpointsbuffer));
3170         }
3171         frontpoints = portalpointsbuffer + portalpointsbufferoffset;
3172         portalpointsbufferoffset += 3*MAX_PORTALPOINTS;
3173         backpoints = portalpointsbuffer + portalpointsbufferoffset;
3174         portalpointsbufferoffset += 3*MAX_PORTALPOINTS;
3175
3176         plane = node->plane;
3177
3178         front = node->children[0];
3179         back = node->children[1];
3180         if (front == back)
3181                 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
3182
3183         // create the new portal by generating a polygon for the node plane,
3184         // and clipping it by all of the other portals(which came from nodes above this one)
3185         nodeportal = (portal_t *)Mem_ExpandableArray_AllocRecord(&portalarray);
3186         nodeportal->plane = *plane;
3187
3188         // 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)
3189         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);
3190         nodeportal->numpoints = 4;
3191         side = 0;       // shut up compiler warning
3192         for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
3193         {
3194                 clipplane = portal->plane;
3195                 if (portal->nodes[0] == portal->nodes[1])
3196                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
3197                 if (portal->nodes[0] == node)
3198                         side = 0;
3199                 else if (portal->nodes[1] == node)
3200                 {
3201                         clipplane.dist = -clipplane.dist;
3202                         VectorNegate(clipplane.normal, clipplane.normal);
3203                         side = 1;
3204                 }
3205                 else
3206                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
3207
3208                 for (i = 0;i < nodeportal->numpoints*3;i++)
3209                         frontpoints[i] = nodeportal->points[i];
3210                 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);
3211                 if (nodeportal->numpoints <= 0 || nodeportal->numpoints >= MAX_PORTALPOINTS)
3212                         break;
3213         }
3214
3215         if (nodeportal->numpoints < 3)
3216         {
3217                 Con_Print("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
3218                 nodeportal->numpoints = 0;
3219         }
3220         else if (nodeportal->numpoints >= MAX_PORTALPOINTS)
3221         {
3222                 Con_Print("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal has too many points\n");
3223                 nodeportal->numpoints = 0;
3224         }
3225
3226         AddPortalToNodes(nodeportal, front, back);
3227
3228         // split the portals of this node along this node's plane and assign them to the children of this node
3229         // (migrating the portals downward through the tree)
3230         for (portal = (portal_t *)node->portals;portal;portal = nextportal)
3231         {
3232                 if (portal->nodes[0] == portal->nodes[1])
3233                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
3234                 if (portal->nodes[0] == node)
3235                         side = 0;
3236                 else if (portal->nodes[1] == node)
3237                         side = 1;
3238                 else
3239                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
3240                 nextportal = portal->next[side];
3241                 if (!portal->numpoints)
3242                         continue;
3243
3244                 other_node = portal->nodes[!side];
3245                 RemovePortalFromNodes(portal);
3246
3247                 // cut the portal into two portals, one on each side of the node plane
3248                 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);
3249
3250                 if (!numfrontpoints)
3251                 {
3252                         if (side == 0)
3253                                 AddPortalToNodes(portal, back, other_node);
3254                         else
3255                                 AddPortalToNodes(portal, other_node, back);
3256                         continue;
3257                 }
3258                 if (!numbackpoints)
3259                 {
3260                         if (side == 0)
3261                                 AddPortalToNodes(portal, front, other_node);
3262                         else
3263                                 AddPortalToNodes(portal, other_node, front);
3264                         continue;
3265                 }
3266
3267                 // the portal is split
3268                 splitportal = (portal_t *)Mem_ExpandableArray_AllocRecord(&portalarray);
3269                 temp = splitportal->chain;
3270                 *splitportal = *portal;
3271                 splitportal->chain = temp;
3272                 for (i = 0;i < numbackpoints*3;i++)
3273                         splitportal->points[i] = backpoints[i];
3274                 splitportal->numpoints = numbackpoints;
3275                 for (i = 0;i < numfrontpoints*3;i++)
3276                         portal->points[i] = frontpoints[i];
3277                 portal->numpoints = numfrontpoints;
3278
3279                 if (side == 0)
3280                 {
3281                         AddPortalToNodes(portal, front, other_node);
3282                         AddPortalToNodes(splitportal, back, other_node);
3283                 }
3284                 else
3285                 {
3286                         AddPortalToNodes(portal, other_node, front);
3287                         AddPortalToNodes(splitportal, other_node, back);
3288                 }
3289         }
3290
3291         Mod_Q1BSP_RecursiveNodePortals(front);
3292         Mod_Q1BSP_RecursiveNodePortals(back);
3293
3294         portalpointsbufferoffset -= 6*MAX_PORTALPOINTS;
3295 }
3296
3297 static void Mod_Q1BSP_MakePortals(void)
3298 {
3299         Mem_ExpandableArray_NewArray(&portalarray, loadmodel->mempool, sizeof(portal_t), 1020*1024/sizeof(portal_t));
3300         portalpointsbufferoffset = 0;
3301         portalpointsbuffersize = 6*MAX_PORTALPOINTS*128;
3302         portalpointsbuffer = Mem_Alloc(loadmodel->mempool, portalpointsbuffersize * sizeof(*portalpointsbuffer));
3303         Mod_Q1BSP_RecursiveNodePortals(loadmodel->brush.data_nodes + loadmodel->brushq1.hulls[0].firstclipnode);
3304         Mem_Free(portalpointsbuffer);
3305         portalpointsbuffer = NULL;
3306         portalpointsbufferoffset = 0;
3307         portalpointsbuffersize = 0;
3308         Mod_Q1BSP_FinalizePortals();
3309         Mem_ExpandableArray_FreeArray(&portalarray);
3310 }
3311
3312 //Returns PVS data for a given point
3313 //(note: can return NULL)
3314 static unsigned char *Mod_Q1BSP_GetPVS(dp_model_t *model, const vec3_t p)
3315 {
3316         mnode_t *node;
3317         node = model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode;
3318         while (node->plane)
3319                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
3320         if (((mleaf_t *)node)->clusterindex >= 0)
3321                 return model->brush.data_pvsclusters + ((mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
3322         else
3323                 return NULL;
3324 }
3325
3326 static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(dp_model_t *model, const vec3_t org, vec_t radius, unsigned char *pvsbuffer, int pvsbytes, mnode_t *node)
3327 {
3328         while (node->plane)
3329         {
3330                 float d = PlaneDiff(org, node->plane);
3331                 if (d > radius)
3332                         node = node->children[0];
3333                 else if (d < -radius)
3334                         node = node->children[1];
3335                 else
3336                 {
3337                         // go down both sides
3338                         Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
3339                         node = node->children[1];
3340                 }
3341         }
3342         // if this leaf is in a cluster, accumulate the pvs bits
3343         if (((mleaf_t *)node)->clusterindex >= 0)
3344         {
3345                 int i;
3346                 unsigned char *pvs = model->brush.data_pvsclusters + ((mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
3347                 for (i = 0;i < pvsbytes;i++)
3348                         pvsbuffer[i] |= pvs[i];
3349         }
3350 }
3351
3352 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
3353 //of the given point.
3354 static int Mod_Q1BSP_FatPVS(dp_model_t *model, const vec3_t org, vec_t radius, unsigned char *pvsbuffer, int pvsbufferlength, qboolean merge)
3355 {
3356         int bytes = model->brush.num_pvsclusterbytes;
3357         bytes = min(bytes, pvsbufferlength);
3358         if (r_novis.integer || !model->brush.num_pvsclusters || !Mod_Q1BSP_GetPVS(model, org))
3359         {
3360                 memset(pvsbuffer, 0xFF, bytes);
3361                 return bytes;
3362         }
3363         if (!merge)
3364                 memset(pvsbuffer, 0, bytes);
3365         Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode);
3366         return bytes;
3367 }
3368
3369 static void Mod_Q1BSP_RoundUpToHullSize(dp_model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs)
3370 {
3371         vec3_t size;
3372         const hull_t *hull;
3373
3374         VectorSubtract(inmaxs, inmins, size);
3375         if (cmodel->brush.ishlbsp)
3376         {
3377                 if (size[0] < 3)
3378                         hull = &cmodel->brushq1.hulls[0]; // 0x0x0
3379                 else if (size[0] <= 32)
3380                 {
3381                         if (size[2] < 54) // pick the nearest of 36 or 72
3382                                 hull = &cmodel->brushq1.hulls[3]; // 32x32x36
3383                         else
3384                                 hull = &cmodel->brushq1.hulls[1]; // 32x32x72
3385                 }
3386                 else
3387                         hull = &cmodel->brushq1.hulls[2]; // 64x64x64
3388         }
3389         else
3390         {
3391                 if (size[0] < 3)
3392                         hull = &cmodel->brushq1.hulls[0]; // 0x0x0
3393                 else if (size[0] <= 32)
3394                         hull = &cmodel->brushq1.hulls[1]; // 32x32x56
3395                 else
3396                         hull = &cmodel->brushq1.hulls[2]; // 64x64x88
3397         }
3398         VectorCopy(inmins, outmins);
3399         VectorAdd(inmins, hull->clip_size, outmaxs);
3400 }
3401
3402 void Mod_Q1BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
3403 {
3404         int i, j, k;
3405         dheader_t *header;
3406         dmodel_t *bm;
3407         float dist, modelyawradius, modelradius;
3408         msurface_t *surface;
3409         int numshadowmeshtriangles;
3410         hullinfo_t hullinfo;
3411         int totalstylesurfaces, totalstyles, stylecounts[256], remapstyles[256];
3412         model_brush_lightstyleinfo_t styleinfo[256];
3413         unsigned char *datapointer;
3414
3415         mod->modeldatatypestring = "Q1BSP";
3416
3417         mod->type = mod_brushq1;
3418
3419         header = (dheader_t *)buffer;
3420
3421         i = LittleLong(header->version);
3422         if (i != BSPVERSION && i != 30)
3423                 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife)", mod->name, i, BSPVERSION);
3424         mod->brush.ishlbsp = i == 30;
3425
3426 // fill in hull info
3427         VectorClear (hullinfo.hullsizes[0][0]);
3428         VectorClear (hullinfo.hullsizes[0][1]);
3429         if (mod->brush.ishlbsp)
3430         {
3431                 mod->modeldatatypestring = "HLBSP";
3432
3433                 hullinfo.filehulls = 4;
3434                 VectorSet (hullinfo.hullsizes[1][0], -16, -16, -36);
3435                 VectorSet (hullinfo.hullsizes[1][1], 16, 16, 36);
3436                 VectorSet (hullinfo.hullsizes[2][0], -32, -32, -32);
3437                 VectorSet (hullinfo.hullsizes[2][1], 32, 32, 32);
3438                 VectorSet (hullinfo.hullsizes[3][0], -16, -16, -18);
3439                 VectorSet (hullinfo.hullsizes[3][1], 16, 16, 18);
3440         }
3441         else
3442         {
3443                 hullinfo.filehulls = 4;
3444                 VectorSet (hullinfo.hullsizes[1][0], -16, -16, -24);
3445                 VectorSet (hullinfo.hullsizes[1][1], 16, 16, 32);
3446                 VectorSet (hullinfo.hullsizes[2][0], -32, -32, -24);
3447                 VectorSet (hullinfo.hullsizes[2][1], 32, 32, 64);
3448         }
3449
3450 // read lumps
3451         mod_base = (unsigned char*)buffer;
3452         for (i = 0; i < HEADER_LUMPS; i++)
3453         {
3454                 header->lumps[i].fileofs = LittleLong(header->lumps[i].fileofs);
3455                 header->lumps[i].filelen = LittleLong(header->lumps[i].filelen);
3456         }
3457
3458         mod->soundfromcenter = true;
3459         mod->TraceBox = Mod_Q1BSP_TraceBox;
3460         mod->TraceLine = Mod_Q1BSP_TraceLine;
3461         mod->TracePoint = Mod_Q1BSP_TracePoint;
3462         mod->PointSuperContents = Mod_Q1BSP_PointSuperContents;
3463         mod->brush.TraceLineOfSight = Mod_Q1BSP_TraceLineOfSight;
3464         mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents;
3465         mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents;
3466         mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
3467         mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
3468         mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
3469         mod->brush.BoxTouchingLeafPVS = Mod_Q1BSP_BoxTouchingLeafPVS;
3470         mod->brush.BoxTouchingVisibleLeafs = Mod_Q1BSP_BoxTouchingVisibleLeafs;
3471         mod->brush.FindBoxClusters = Mod_Q1BSP_FindBoxClusters;
3472         mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
3473         mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
3474         mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint;
3475         mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize;
3476         mod->brush.PointInLeaf = Mod_Q1BSP_PointInLeaf;
3477         mod->Draw = R_Q1BSP_Draw;
3478         mod->DrawDepth = R_Q1BSP_DrawDepth;
3479         mod->DrawDebug = R_Q1BSP_DrawDebug;
3480         mod->DrawPrepass = R_Q1BSP_DrawPrepass;
3481         mod->GetLightInfo = R_Q1BSP_GetLightInfo;
3482         mod->CompileShadowMap = R_Q1BSP_CompileShadowMap;
3483         mod->DrawShadowMap = R_Q1BSP_DrawShadowMap;
3484         mod->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
3485         mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
3486         mod->DrawLight = R_Q1BSP_DrawLight;
3487
3488 // load into heap
3489
3490         mod->brush.qw_md4sum = 0;
3491         mod->brush.qw_md4sum2 = 0;
3492         for (i = 0;i < HEADER_LUMPS;i++)
3493         {
3494                 int temp;
3495                 if (i == LUMP_ENTITIES)
3496                         continue;
3497                 temp = Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen);
3498                 mod->brush.qw_md4sum ^= LittleLong(temp);
3499                 if (i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES)
3500                         continue;
3501                 temp = Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen);
3502                 mod->brush.qw_md4sum2 ^= LittleLong(temp);
3503         }
3504
3505         Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
3506         Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
3507         Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
3508         Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
3509         Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
3510         Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
3511         Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
3512         Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
3513         Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
3514         Mod_Q1BSP_LoadLeaffaces(&header->lumps[LUMP_MARKSURFACES]);
3515         Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
3516         // load submodels before leafs because they contain the number of vis leafs
3517         Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS], &hullinfo);
3518         Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
3519         Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
3520         Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES], &hullinfo);
3521
3522         // check if the map supports transparent water rendering
3523         loadmodel->brush.supportwateralpha = Mod_Q1BSP_CheckWaterAlphaSupport();
3524
3525         if (mod->brushq1.data_compressedpvs)
3526                 Mem_Free(mod->brushq1.data_compressedpvs);
3527         mod->brushq1.data_compressedpvs = NULL;
3528         mod->brushq1.num_compressedpvs = 0;
3529
3530         Mod_Q1BSP_MakeHull0();
3531         Mod_Q1BSP_MakePortals();
3532
3533         mod->numframes = 2;             // regular and alternate animation
3534         mod->numskins = 1;
3535
3536         // make a single combined shadow mesh to allow optimized shadow volume creation
3537         numshadowmeshtriangles = 0;
3538         for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
3539         {
3540                 surface->num_firstshadowmeshtriangle = numshadowmeshtriangles;
3541                 numshadowmeshtriangles += surface->num_triangles;
3542         }
3543         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
3544         for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
3545                 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));
3546         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true, false);
3547         Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
3548
3549         if (loadmodel->brush.numsubmodels)
3550                 loadmodel->brush.submodels = (dp_model_t **)Mem_Alloc(loadmodel->mempool, loadmodel->brush.numsubmodels * sizeof(dp_model_t *));
3551
3552         // LordHavoc: to clear the fog around the original quake submodel code, I
3553         // will explain:
3554         // first of all, some background info on the submodels:
3555         // model 0 is the map model (the world, named maps/e1m1.bsp for example)
3556         // model 1 and higher are submodels (doors and the like, named *1, *2, etc)
3557         // now the weird for loop itself:
3558         // the loop functions in an odd way, on each iteration it sets up the
3559         // current 'mod' model (which despite the confusing code IS the model of
3560         // the number i), at the end of the loop it duplicates the model to become
3561         // the next submodel, and loops back to set up the new submodel.
3562
3563         // LordHavoc: now the explanation of my sane way (which works identically):
3564         // set up the world model, then on each submodel copy from the world model
3565         // and set up the submodel with the respective model info.
3566         totalstylesurfaces = 0;
3567         totalstyles = 0;
3568         for (i = 0;i < mod->brush.numsubmodels;i++)
3569         {
3570                 memset(stylecounts, 0, sizeof(stylecounts));
3571                 for (k = 0;k < mod->brushq1.submodels[i].numfaces;k++)
3572                 {
3573                         surface = mod->data_surfaces + mod->brushq1.submodels[i].firstface + k;
3574                         for (j = 0;j < MAXLIGHTMAPS;j++)
3575                                 stylecounts[surface->lightmapinfo->styles[j]]++;
3576                 }
3577                 for (k = 0;k < 255;k++)
3578                 {
3579                         totalstyles++;
3580                         if (stylecounts[k])
3581                                 totalstylesurfaces += stylecounts[k];
3582                 }
3583         }
3584         datapointer = (unsigned char *)Mem_Alloc(mod->mempool, mod->num_surfaces * sizeof(int) + totalstyles * sizeof(model_brush_lightstyleinfo_t) + totalstylesurfaces * sizeof(int *));
3585         for (i = 0;i < mod->brush.numsubmodels;i++)
3586         {
3587                 // LordHavoc: this code was originally at the end of this loop, but
3588                 // has been transformed to something more readable at the start here.
3589
3590                 if (i > 0)
3591                 {
3592                         char name[10];
3593                         // duplicate the basic information
3594                         dpsnprintf(name, sizeof(name), "*%i", i);
3595                         mod = Mod_FindName(name, loadmodel->name);
3596                         // copy the base model to this one
3597                         *mod = *loadmodel;
3598                         // rename the clone back to its proper name
3599                         strlcpy(mod->name, name, sizeof(mod->name));
3600                         mod->brush.parentmodel = loadmodel;
3601                         // textures and memory belong to the main model
3602                         mod->texturepool = NULL;
3603                         mod->mempool = NULL;
3604                         mod->brush.GetPVS = NULL;
3605                         mod->brush.FatPVS = NULL;
3606                         mod->brush.BoxTouchingPVS = NULL;
3607                         mod->brush.BoxTouchingLeafPVS = NULL;
3608                         mod->brush.BoxTouchingVisibleLeafs = NULL;
3609                         mod->brush.FindBoxClusters = NULL;
3610                         mod->brush.LightPoint = NULL;
3611                         mod->brush.AmbientSoundLevelsForPoint = NULL;
3612                 }
3613
3614                 mod->brush.submodel = i;
3615
3616                 if (loadmodel->brush.submodels)
3617                         loadmodel->brush.submodels[i] = mod;
3618
3619                 bm = &mod->brushq1.submodels[i];
3620
3621                 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
3622                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
3623                 {
3624                         mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
3625                         mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
3626                 }
3627
3628                 mod->firstmodelsurface = bm->firstface;
3629                 mod->nummodelsurfaces = bm->numfaces;
3630
3631                 // set node/leaf parents for this submodel
3632                 Mod_Q1BSP_LoadNodes_RecursiveSetParent(mod->brush.data_nodes + mod->brushq1.hulls[0].firstclipnode, NULL);
3633
3634                 // make the model surface list (used by shadowing/lighting)
3635                 mod->sortedmodelsurfaces = (int *)datapointer;datapointer += mod->nummodelsurfaces * sizeof(int);
3636                 Mod_MakeSortedSurfaces(mod);
3637
3638                 // copy the submodel bounds, then enlarge the yaw and rotated bounds according to radius
3639                 // (previously this code measured the radius of the vertices of surfaces in the submodel, but that broke submodels that contain only CLIP brushes, which do not produce surfaces)
3640                 VectorCopy(bm->mins, mod->normalmins);
3641                 VectorCopy(bm->maxs, mod->normalmaxs);
3642                 dist = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
3643                 modelyawradius = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
3644                 modelyawradius = dist*dist+modelyawradius*modelyawradius;
3645                 modelradius = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
3646                 modelradius = modelyawradius + modelradius * modelradius;
3647                 modelyawradius = sqrt(modelyawradius);
3648                 modelradius = sqrt(modelradius);
3649                 mod->yawmins[0] = mod->yawmins[1] = -modelyawradius;
3650                 mod->yawmins[2] = mod->normalmins[2];
3651                 mod->yawmaxs[0] = mod->yawmaxs[1] =  modelyawradius;
3652                 mod->yawmaxs[2] = mod->normalmaxs[2];
3653                 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
3654                 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] =  modelradius;
3655                 mod->radius = modelradius;
3656                 mod->radius2 = modelradius * modelradius;
3657
3658                 // this gets altered below if sky or water is used
3659                 mod->DrawSky = NULL;
3660                 mod->DrawAddWaterPlanes = NULL;
3661
3662                 // scan surfaces for sky and water and flag the submodel as possessing these features or not
3663                 // build lightstyle lists for quick marking of dirty lightmaps when lightstyles flicker
3664                 if (mod->nummodelsurfaces)
3665                 {
3666                         for (j = 0, surface = &mod->data_surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surface++)
3667                                 if (surface->texture->basematerialflags & MATERIALFLAG_SKY)
3668                                         break;
3669                         if (j < mod->nummodelsurfaces)
3670                                 mod->DrawSky = R_Q1BSP_DrawSky;
3671
3672                         for (j = 0, surface = &mod->data_surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surface++)
3673                                 if (surface->texture->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION))
3674                                         break;
3675                         if (j < mod->nummodelsurfaces)
3676                                 mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
3677
3678                         // build lightstyle update chains
3679                         // (used to rapidly mark lightmapupdateflags on many surfaces
3680                         // when d_lightstylevalue changes)
3681                         memset(stylecounts, 0, sizeof(stylecounts));
3682                         for (k = 0;k < mod->nummodelsurfaces;k++)
3683                         {
3684                                 surface = mod->data_surfaces + mod->firstmodelsurface + k;
3685                                 for (j = 0;j < MAXLIGHTMAPS;j++)
3686                                         stylecounts[surface->lightmapinfo->styles[j]]++;
3687                         }
3688                         mod->brushq1.num_lightstyles = 0;
3689                         for (k = 0;k < 255;k++)
3690                         {
3691                                 if (stylecounts[k])
3692                                 {
3693                                         styleinfo[mod->brushq1.num_lightstyles].style = k;
3694                                         styleinfo[mod->brushq1.num_lightstyles].value = 0;
3695                                         styleinfo[mod->brushq1.num_lightstyles].numsurfaces = 0;
3696                                         styleinfo[mod->brushq1.num_lightstyles].surfacelist = (int *)datapointer;datapointer += stylecounts[k] * sizeof(int);
3697                                         remapstyles[k] = mod->brushq1.num_lightstyles;
3698                                         mod->brushq1.num_lightstyles++;
3699                                 }
3700                         }
3701                         for (k = 0;k < mod->nummodelsurfaces;k++)
3702                         {
3703                                 surface = mod->data_surfaces + mod->firstmodelsurface + k;
3704                                 for (j = 0;j < MAXLIGHTMAPS;j++)
3705                                 {
3706                                         if (surface->lightmapinfo->styles[j] != 255)
3707                                         {
3708                                                 int r = remapstyles[surface->lightmapinfo->styles[j]];
3709                                                 styleinfo[r].surfacelist[styleinfo[r].numsurfaces++] = mod->firstmodelsurface + k;
3710                                         }
3711                                 }
3712                         }
3713                         mod->brushq1.data_lightstyleinfo = (model_brush_lightstyleinfo_t *)datapointer;datapointer += mod->brushq1.num_lightstyles * sizeof(model_brush_lightstyleinfo_t);
3714                         memcpy(mod->brushq1.data_lightstyleinfo, styleinfo, mod->brushq1.num_lightstyles * sizeof(model_brush_lightstyleinfo_t));
3715                 }
3716                 else
3717                 {
3718                         // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
3719                         Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadmodel->name);
3720                 }
3721                 //mod->brushq1.num_visleafs = bm->visleafs;
3722
3723                 // generate VBOs and other shared data before cloning submodels
3724                 if (i == 0)
3725                 {
3726                         Mod_BuildVBOs();
3727                         Mod_Q1BSP_LoadMapBrushes();
3728                         //Mod_Q1BSP_ProcessLightList();
3729                 }
3730         }
3731
3732         Con_DPrintf("Stats for q1bsp model \"%s\": %i faces, %i nodes, %i leafs, %i visleafs, %i visleafportals, mesh: %i vertices, %i triangles, %i surfaces\n", loadmodel->name, loadmodel->num_surfaces, loadmodel->brush.num_nodes, loadmodel->brush.num_leafs, mod->brush.num_pvsclusters, loadmodel->brush.num_portals, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->num_surfaces);
3733 }
3734
3735 static void Mod_Q2BSP_LoadEntities(lump_t *l)
3736 {
3737 }
3738
3739 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
3740 {
3741 /*
3742         d_t *in;
3743         m_t *out;
3744         int i, count;
3745
3746         in = (void *)(mod_base + l->fileofs);
3747         if (l->filelen % sizeof(*in))
3748                 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3749         count = l->filelen / sizeof(*in);
3750         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3751
3752         loadmodel-> = out;
3753         loadmodel->num = count;
3754
3755         for (i = 0;i < count;i++, in++, out++)
3756         {
3757         }
3758 */
3759 }
3760
3761 static void Mod_Q2BSP_LoadVertices(lump_t *l)
3762 {
3763 /*
3764         d_t *in;
3765         m_t *out;
3766         int i, count;
3767
3768         in = (void *)(mod_base + l->fileofs);
3769         if (l->filelen % sizeof(*in))
3770                 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3771         count = l->filelen / sizeof(*in);
3772         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3773
3774         loadmodel-> = out;
3775         loadmodel->num = count;
3776
3777         for (i = 0;i < count;i++, in++, out++)
3778         {
3779         }
3780 */
3781 }
3782
3783 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
3784 {
3785 /*
3786         d_t *in;
3787         m_t *out;
3788         int i, count;
3789
3790         in = (void *)(mod_base + l->fileofs);
3791         if (l->filelen % sizeof(*in))
3792                 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
3793         count = l->filelen / sizeof(*in);
3794         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3795
3796         loadmodel-> = out;
3797         loadmodel->num = count;
3798
3799         for (i = 0;i < count;i++, in++, out++)
3800         {
3801         }
3802 */
3803 }
3804
3805 static void Mod_Q2BSP_LoadNodes(lump_t *l)
3806 {
3807 /*
3808         d_t *in;
3809         m_t *out;
3810         int i, count;
3811
3812         in = (void *)(mod_base + l->fileofs);
3813         if (l->filelen % sizeof(*in))
3814                 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3815         count = l->filelen / sizeof(*in);
3816         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3817
3818         loadmodel-> = out;
3819         loadmodel->num = count;
3820
3821         for (i = 0;i < count;i++, in++, out++)
3822         {
3823         }
3824 */
3825 }
3826
3827 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
3828 {
3829 /*
3830         d_t *in;
3831         m_t *out;
3832         int i, count;
3833
3834         in = (void *)(mod_base + l->fileofs);
3835         if (l->filelen % sizeof(*in))
3836                 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
3837         count = l->filelen / sizeof(*in);
3838         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3839
3840         loadmodel-> = out;
3841         loadmodel->num = count;
3842
3843         for (i = 0;i < count;i++, in++, out++)
3844         {
3845         }
3846 */
3847 }
3848
3849 static void Mod_Q2BSP_LoadFaces(lump_t *l)
3850 {
3851 /*
3852         d_t *in;
3853         m_t *out;
3854         int i, count;
3855
3856         in = (void *)(mod_base + l->fileofs);
3857         if (l->filelen % sizeof(*in))
3858                 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3859         count = l->filelen / sizeof(*in);
3860         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3861
3862         loadmodel-> = out;
3863         loadmodel->num = count;
3864
3865         for (i = 0;i < count;i++, in++, out++)
3866         {
3867         }
3868 */
3869 }
3870
3871 static void Mod_Q2BSP_LoadLighting(lump_t *l)
3872 {
3873 /*
3874         d_t *in;
3875         m_t *out;
3876         int i, count;
3877
3878         in = (void *)(mod_base + l->fileofs);
3879         if (l->filelen % sizeof(*in))
3880                 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
3881         count = l->filelen / sizeof(*in);
3882         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3883
3884         loadmodel-> = out;
3885         loadmodel->num = count;
3886
3887         for (i = 0;i < count;i++, in++, out++)
3888         {
3889         }
3890 */
3891 }
3892
3893 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
3894 {
3895 /*
3896         d_t *in;
3897         m_t *out;
3898         int i, count;
3899
3900         in = (void *)(mod_base + l->fileofs);
3901         if (l->filelen % sizeof(*in))
3902                 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3903         count = l->filelen / sizeof(*in);
3904         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3905
3906         loadmodel-> = out;
3907         loadmodel->num = count;
3908
3909         for (i = 0;i < count;i++, in++, out++)
3910         {
3911         }
3912 */
3913 }
3914
3915 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3916 {
3917 /*
3918         d_t *in;
3919         m_t *out;
3920         int i, count;
3921
3922         in = (void *)(mod_base + l->fileofs);
3923         if (l->filelen % sizeof(*in))
3924                 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3925         count = l->filelen / sizeof(*in);
3926         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3927
3928         loadmodel-> = out;
3929         loadmodel->num = count;
3930
3931         for (i = 0;i < count;i++, in++, out++)
3932         {
3933         }
3934 */
3935 }
3936
3937 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3938 {
3939 /*
3940         d_t *in;
3941         m_t *out;
3942         int i, count;
3943
3944         in = (void *)(mod_base + l->fileofs);
3945         if (l->filelen % sizeof(*in))
3946                 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3947         count = l->filelen / sizeof(*in);
3948         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3949
3950         loadmodel-> = out;
3951         loadmodel->num = count;
3952
3953         for (i = 0;i < count;i++, in++, out++)
3954         {
3955         }
3956 */
3957 }
3958
3959 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3960 {
3961 /*
3962         d_t *in;
3963         m_t *out;
3964         int i, count;
3965
3966         in = (void *)(mod_base + l->fileofs);
3967         if (l->filelen % sizeof(*in))
3968                 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3969         count = l->filelen / sizeof(*in);
3970         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3971
3972         loadmodel-> = out;
3973         loadmodel->num = count;
3974
3975         for (i = 0;i < count;i++, in++, out++)
3976         {
3977         }
3978 */
3979 }
3980
3981 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3982 {
3983 /*
3984         d_t *in;
3985         m_t *out;
3986         int i, count;
3987
3988         in = (void *)(mod_base + l->fileofs);
3989         if (l->filelen % sizeof(*in))
3990                 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3991         count = l->filelen / sizeof(*in);
3992         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3993
3994         loadmodel-> = out;
3995         loadmodel->num = count;
3996
3997         for (i = 0;i < count;i++, in++, out++)
3998         {
3999         }
4000 */
4001 }
4002
4003 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
4004 {
4005 /*
4006         d_t *in;
4007         m_t *out;
4008         int i, count;
4009
4010         in = (void *)(mod_base + l->fileofs);
4011         if (l->filelen % sizeof(*in))
4012                 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
4013         count = l->filelen / sizeof(*in);
4014         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4015
4016         loadmodel-> = out;
4017         loadmodel->num = count;
4018
4019         for (i = 0;i < count;i++, in++, out++)
4020         {
4021         }
4022 */
4023 }
4024
4025 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
4026 {
4027 /*
4028         d_t *in;
4029         m_t *out;
4030         int i, count;
4031
4032         in = (void *)(mod_base + l->fileofs);
4033         if (l->filelen % sizeof(*in))
4034                 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
4035         count = l->filelen / sizeof(*in);
4036         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4037
4038         loadmodel-> = out;
4039         loadmodel->num = count;
4040
4041         for (i = 0;i < count;i++, in++, out++)
4042         {
4043         }
4044 */
4045 }
4046
4047 static void Mod_Q2BSP_LoadAreas(lump_t *l)
4048 {
4049 /*
4050         d_t *in;
4051         m_t *out;
4052         int i, count;
4053
4054         in = (void *)(mod_base + l->fileofs);
4055         if (l->filelen % sizeof(*in))
4056                 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
4057         count = l->filelen / sizeof(*in);
4058         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4059
4060         loadmodel-> = out;
4061         loadmodel->num = count;
4062
4063         for (i = 0;i < count;i++, in++, out++)
4064         {
4065         }
4066 */
4067 }
4068
4069 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
4070 {
4071 /*
4072         d_t *in;
4073         m_t *out;
4074         int i, count;
4075
4076         in = (void *)(mod_base + l->fileofs);
4077         if (l->filelen % sizeof(*in))
4078                 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
4079         count = l->filelen / sizeof(*in);
4080         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4081
4082         loadmodel-> = out;
4083         loadmodel->num = count;
4084
4085         for (i = 0;i < count;i++, in++, out++)
4086         {
4087         }
4088 */
4089 }
4090
4091 static void Mod_Q2BSP_LoadModels(lump_t *l)
4092 {
4093 /*
4094         d_t *in;
4095         m_t *out;
4096         int i, count;
4097
4098         in = (void *)(mod_base + l->fileofs);
4099         if (l->filelen % sizeof(*in))
4100                 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
4101         count = l->filelen / sizeof(*in);
4102         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4103
4104         loadmodel-> = out;
4105         loadmodel->num = count;
4106
4107         for (i = 0;i < count;i++, in++, out++)
4108         {
4109         }
4110 */
4111 }
4112
4113 void static Mod_Q2BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
4114 {
4115         int i;
4116         q2dheader_t *header;
4117
4118         Host_Error("Mod_Q2BSP_Load: not yet implemented");
4119
4120         mod->modeldatatypestring = "Q2BSP";
4121
4122         mod->type = mod_brushq2;
4123
4124         header = (q2dheader_t *)buffer;
4125
4126         i = LittleLong(header->version);
4127         if (i != Q2BSPVERSION)
4128                 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
4129
4130         mod_base = (unsigned char *)header;
4131
4132         // swap all the lumps
4133         for (i = 0;i < (int) sizeof(*header) / 4;i++)
4134                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
4135
4136         mod->brush.qw_md4sum = 0;
4137         mod->brush.qw_md4sum2 = 0;
4138         for (i = 0;i < Q2HEADER_LUMPS;i++)
4139         {
4140                 if (i == Q2LUMP_ENTITIES)
4141                         continue;
4142                 mod->brush.qw_md4sum ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen);
4143                 if (i == Q2LUMP_VISIBILITY || i == Q2LUMP_LEAFS || i == Q2LUMP_NODES)
4144                         continue;
4145                 mod->brush.qw_md4sum2 ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen);
4146         }
4147
4148         Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
4149         Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
4150         Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
4151         Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
4152         Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
4153         Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
4154         Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
4155         Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
4156         Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
4157         Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
4158         Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
4159         Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
4160         Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
4161         Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
4162         Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
4163         Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
4164         Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
4165         // LordHavoc: must go last because this makes the submodels
4166         Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
4167 }
4168
4169 static int Mod_Q3BSP_SuperContentsFromNativeContents(dp_model_t *model, int nativecontents);
4170 static int Mod_Q3BSP_NativeContentsFromSuperContents(dp_model_t *model, int supercontents);
4171
4172 static void Mod_Q3BSP_LoadEntities(lump_t *l)
4173 {
4174         const char *data;
4175         char key[128], value[MAX_INPUTLINE];
4176         float v[3];
4177         loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
4178         loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
4179         loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
4180         if (!l->filelen)
4181                 return;
4182         loadmodel->brush.entities = (char *)Mem_Alloc(loadmodel->mempool, l->filelen + 1);
4183         memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
4184         loadmodel->brush.entities[l->filelen] = 0;
4185         data = loadmodel->brush.entities;
4186         // some Q3 maps override the lightgrid_cellsize with a worldspawn key
4187         // VorteX: q3map2 FS-R generates tangentspace deluxemaps for q3bsp and sets 'deluxeMaps' key
4188         loadmodel->brushq3.deluxemapping = false;
4189         if (data && COM_ParseToken_Simple(&data, false, false) && com_token[0] == '{')
4190         {
4191                 while (1)
4192                 {
4193                         if (!COM_ParseToken_Simple(&data, false, false))
4194                                 break; // error
4195                         if (com_token[0] == '}')
4196                                 break; // end of worldspawn
4197                         if (com_token[0] == '_')
4198                                 strlcpy(key, com_token + 1, sizeof(key));
4199                         else
4200                                 strlcpy(key, com_token, sizeof(key));
4201                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
4202                                 key[strlen(key)-1] = 0;
4203                         if (!COM_ParseToken_Simple(&data, false, false))
4204                                 break; // error
4205                         strlcpy(value, com_token, sizeof(value));
4206                         if (!strcasecmp("gridsize", key)) // this one is case insensitive to 100% match q3map2
4207                         {
4208 #if _MSC_VER >= 1400
4209 #define sscanf sscanf_s
4210 #endif
4211 #if 0
4212                                 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
4213                                         VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
4214 #else
4215                                 VectorSet(v, 64, 64, 128);
4216                                 if(sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) != 3)
4217                                         Con_Printf("Mod_Q3BSP_LoadEntities: funny gridsize \"%s\" in %s, interpreting as \"%f %f %f\" to match q3map2's parsing\n", value, loadmodel->name, v[0], v[1], v[2]);
4218                                 if (v[0] != 0 && v[1] != 0 && v[2] != 0)
4219                                         VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
4220 #endif
4221                         }
4222                         else if (!strcmp("deluxeMaps", key))
4223                         {
4224                                 if (!strcmp(com_token, "1"))
4225                                 {
4226                                         loadmodel->brushq3.deluxemapping = true;
4227                                         loadmodel->brushq3.deluxemapping_modelspace = true;
4228                                 }
4229                                 else if (!strcmp(com_token, "2"))
4230                                 {
4231                                         loadmodel->brushq3.deluxemapping = true;
4232                                         loadmodel->brushq3.deluxemapping_modelspace = false;
4233                                 }
4234                         }
4235                 }
4236         }
4237 }
4238
4239 static void Mod_Q3BSP_LoadTextures(lump_t *l)
4240 {
4241         q3dtexture_t *in;
4242         texture_t *out;
4243         int i, count;
4244
4245         in = (q3dtexture_t *)(mod_base + l->fileofs);
4246         if (l->filelen % sizeof(*in))
4247                 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
4248         count = l->filelen / sizeof(*in);
4249         out = (texture_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4250
4251         loadmodel->data_textures = out;
4252         loadmodel->num_textures = count;
4253         loadmodel->num_texturesperskin = loadmodel->num_textures;
4254
4255         for (i = 0;i < count;i++)
4256         {
4257                 strlcpy (out[i].name, in[i].name, sizeof (out[i].name));
4258                 out[i].surfaceflags = LittleLong(in[i].surfaceflags);
4259                 out[i].supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, LittleLong(in[i].contents));
4260         }
4261
4262         if (cls.state == ca_dedicated)
4263                 return;
4264
4265         for (i = 0;i < count;i++, in++, out++)
4266                 Mod_LoadTextureFromQ3Shader(out, out->name, true, true, TEXF_MIPMAP | (r_picmipworld.integer ? TEXF_PICMIP : 0) | TEXF_COMPRESS);
4267 }
4268
4269 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
4270 {
4271         q3dplane_t *in;
4272         mplane_t *out;
4273         int i, count;
4274
4275         in = (q3dplane_t *)(mod_base + l->fileofs);
4276         if (l->filelen % sizeof(*in))
4277                 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
4278         count = l->filelen / sizeof(*in);
4279         out = (mplane_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4280
4281         loadmodel->brush.data_planes = out;
4282         loadmodel->brush.num_planes = count;
4283
4284         for (i = 0;i < count;i++, in++, out++)
4285         {
4286                 out->normal[0] = LittleFloat(in->normal[0]);
4287                 out->normal[1] = LittleFloat(in->normal[1]);
4288                 out->normal[2] = LittleFloat(in->normal[2]);
4289                 out->dist = LittleFloat(in->dist);
4290                 PlaneClassify(out);
4291         }
4292 }
4293
4294 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
4295 {
4296         q3dbrushside_t *in;
4297         q3mbrushside_t *out;
4298         int i, n, count;
4299
4300         in = (q3dbrushside_t *)(mod_base + l->fileofs);
4301         if (l->filelen % sizeof(*in))
4302                 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
4303         count = l->filelen / sizeof(*in);
4304         out = (q3mbrushside_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4305
4306         loadmodel->brush.data_brushsides = out;
4307         loadmodel->brush.num_brushsides = count;
4308
4309         for (i = 0;i < count;i++, in++, out++)
4310         {
4311                 n = LittleLong(in->planeindex);
4312                 if (n < 0 || n >= loadmodel->brush.num_planes)
4313                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)", n, loadmodel->brush.num_planes);
4314                 out->plane = loadmodel->brush.data_planes + n;
4315                 n = LittleLong(in->textureindex);
4316                 if (n < 0 || n >= loadmodel->num_textures)
4317                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)", n, loadmodel->num_textures);
4318                 out->texture = loadmodel->data_textures + n;
4319         }
4320 }
4321
4322 static void Mod_Q3BSP_LoadBrushSides_IG(lump_t *l)
4323 {
4324         q3dbrushside_ig_t *in;
4325         q3mbrushside_t *out;
4326         int i, n, count;
4327
4328         in = (q3dbrushside_ig_t *)(mod_base + l->fileofs);
4329         if (l->filelen % sizeof(*in))
4330                 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
4331         count = l->filelen / sizeof(*in);
4332         out = (q3mbrushside_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4333
4334         loadmodel->brush.data_brushsides = out;
4335         loadmodel->brush.num_brushsides = count;
4336
4337         for (i = 0;i < count;i++, in++, out++)
4338         {
4339                 n = LittleLong(in->planeindex);
4340                 if (n < 0 || n >= loadmodel->brush.num_planes)
4341                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)", n, loadmodel->brush.num_planes);
4342                 out->plane = loadmodel->brush.data_planes + n;
4343                 n = LittleLong(in->textureindex);
4344                 if (n < 0 || n >= loadmodel->num_textures)
4345                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)", n, loadmodel->num_textures);
4346                 out->texture = loadmodel->data_textures + n;
4347         }
4348 }
4349
4350 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
4351 {
4352         q3dbrush_t *in;
4353         q3mbrush_t *out;
4354         int i, j, n, c, count, maxplanes, q3surfaceflags;
4355         colplanef_t *planes;
4356
4357         in = (q3dbrush_t *)(mod_base + l->fileofs);
4358         if (l->filelen % sizeof(*in))
4359                 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
4360         count = l->filelen / sizeof(*in);
4361         out = (q3mbrush_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4362
4363         loadmodel->brush.data_brushes = out;
4364         loadmodel->brush.num_brushes = count;
4365
4366         maxplanes = 0;
4367         planes = NULL;
4368
4369         for (i = 0;i < count;i++, in++, out++)
4370         {
4371                 n = LittleLong(in->firstbrushside);
4372                 c = LittleLong(in->numbrushsides);
4373                 if (n < 0 || n + c > loadmodel->brush.num_brushsides)
4374                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)", n, n + c, loadmodel->brush.num_brushsides);
4375                 out->firstbrushside = loadmodel->brush.data_brushsides + n;
4376                 out->numbrushsides = c;
4377                 n = LittleLong(in->textureindex);
4378                 if (n < 0 || n >= loadmodel->num_textures)
4379                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)", n, loadmodel->num_textures);
4380                 out->texture = loadmodel->data_textures + n;
4381
4382                 // make a list of mplane_t structs to construct a colbrush from
4383                 if (maxplanes < out->numbrushsides)
4384                 {
4385                         maxplanes = out->numbrushsides;
4386                         if (planes)
4387                                 Mem_Free(planes);
4388                         planes = (colplanef_t *)Mem_Alloc(tempmempool, sizeof(colplanef_t) * maxplanes);
4389                 }
4390                 q3surfaceflags = 0;
4391                 for (j = 0;j < out->numbrushsides;j++)
4392                 {
4393                         VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
4394                         planes[j].dist = out->firstbrushside[j].plane->dist;
4395                         planes[j].q3surfaceflags = out->firstbrushside[j].texture->surfaceflags;
4396                         planes[j].texture = out->firstbrushside[j].texture;
4397                         q3surfaceflags |= planes[j].q3surfaceflags;
4398                 }
4399                 // make the colbrush from the planes
4400                 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents, q3surfaceflags, out->texture, true);
4401
4402                 // this whole loop can take a while (e.g. on redstarrepublic4)
4403                 CL_KeepaliveMessage(false);
4404         }
4405         if (planes)
4406                 Mem_Free(planes);
4407 }
4408
4409 static void Mod_Q3BSP_LoadEffects(lump_t *l)
4410 {
4411         q3deffect_t *in;
4412         q3deffect_t *out;
4413         int i, n, count;
4414
4415         in = (q3deffect_t *)(mod_base + l->fileofs);
4416         if (l->filelen % sizeof(*in))
4417                 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
4418         count = l->filelen / sizeof(*in);
4419         out = (q3deffect_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4420
4421         loadmodel->brushq3.data_effects = out;
4422         loadmodel->brushq3.num_effects = count;
4423
4424         for (i = 0;i < count;i++, in++, out++)
4425         {
4426                 strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
4427                 n = LittleLong(in->brushindex);
4428                 if (n >= loadmodel->brush.num_brushes)
4429                 {
4430                         Con_Printf("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes), setting to -1\n", n, loadmodel->brush.num_brushes);
4431                         n = -1;
4432                 }
4433                 out->brushindex = n;
4434                 out->unknown = LittleLong(in->unknown);
4435         }
4436 }
4437
4438 static void Mod_Q3BSP_LoadVertices(lump_t *l)
4439 {
4440         q3dvertex_t *in;
4441         int i, count;
4442
4443         in = (q3dvertex_t *)(mod_base + l->fileofs);
4444         if (l->filelen % sizeof(*in))
4445                 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
4446         loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
4447         loadmodel->brushq3.data_vertex3f = (float *)Mem_Alloc(loadmodel->mempool, count * (sizeof(float) * (3 + 3 + 2 + 2 + 4)));
4448         loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_vertex3f + count * 3;
4449         loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_normal3f + count * 3;
4450         loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + count * 2;
4451         loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_texcoordlightmap2f + count * 2;
4452
4453         for (i = 0;i < count;i++, in++)
4454         {
4455                 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
4456                 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
4457                 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
4458                 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
4459                 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
4460                 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
4461                 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
4462                 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
4463                 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
4464                 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
4465                 // svector/tvector are calculated later in face loading
4466                 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
4467                 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
4468                 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
4469                 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
4470         }
4471 }
4472
4473 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
4474 {
4475         int *in;
4476         int *out;
4477         int i, count;
4478
4479         in = (int *)(mod_base + l->fileofs);
4480         if (l->filelen % sizeof(int[3]))
4481                 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
4482         count = l->filelen / sizeof(*in);
4483
4484         if(!loadmodel->brushq3.num_vertices)
4485         {
4486                 if (count)
4487                         Con_Printf("Mod_Q3BSP_LoadTriangles: %s has triangles but no vertexes, broken compiler, ignoring problem\n", loadmodel->name);
4488                 loadmodel->brushq3.num_triangles = 0;
4489                 return;
4490         }
4491
4492         out = (int *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4493         loadmodel->brushq3.num_triangles = count / 3;
4494         loadmodel->brushq3.data_element3i = out;
4495
4496         for (i = 0;i < count;i++, in++, out++)
4497         {
4498                 *out = LittleLong(*in);
4499                 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
4500                 {
4501                         Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
4502                         *out = 0;
4503                 }
4504         }
4505 }
4506
4507 static void Mod_Q3BSP_LoadLightmaps(lump_t *l, lump_t *faceslump)
4508 {
4509         q3dlightmap_t *input_pointer;
4510         int i, j, k, count, power, power2, endlightmap, mergewidth, mergeheight;
4511         unsigned char *c;
4512
4513         unsigned char *convertedpixels;
4514         char mapname[MAX_QPATH];
4515         int size, bytesperpixel, rgbmap[3];
4516         qboolean external;
4517         unsigned char *inpixels[10000]; // max count q3map2 can output (it uses 4 digits)
4518
4519         // defaults for q3bsp
4520         size = 128;
4521         bytesperpixel = 3;
4522         rgbmap[0] = 2;
4523         rgbmap[1] = 1;
4524         rgbmap[2] = 0;
4525         external = false;
4526         loadmodel->brushq3.lightmapsize = 128;
4527
4528         if (cls.state == ca_dedicated)
4529                 return;
4530
4531         if(mod_q3bsp_nolightmaps.integer)
4532         {
4533                 return;
4534         }
4535         else if(l->filelen)
4536         {
4537                 // prefer internal LMs for compatibility (a BSP contains no info on whether external LMs exist)
4538                 if (developer_loading.integer)
4539                         Con_Printf("Using internal lightmaps\n");
4540                 input_pointer = (q3dlightmap_t *)(mod_base + l->fileofs);
4541                 if (l->filelen % sizeof(*input_pointer))
4542                         Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
4543                 count = l->filelen / sizeof(*input_pointer);
4544                 for(i = 0; i < count; ++i)
4545                         inpixels[i] = input_pointer[i].rgb;
4546         }
4547         else
4548         {
4549                 // no internal lightmaps
4550                 // try external lightmaps
4551                 if (developer_loading.integer)
4552                         Con_Printf("Using external lightmaps\n");
4553                 FS_StripExtension(loadmodel->name, mapname, sizeof(mapname));
4554                 inpixels[0] = loadimagepixelsbgra(va("%s/lm_%04d", mapname, 0), false, false, false);
4555                 if(!inpixels[0])
4556                         return;
4557
4558                 // using EXTERNAL lightmaps instead
4559                 if(image_width != (int) CeilPowerOf2(image_width) || image_width != image_height)
4560                 {
4561                         Mem_Free(inpixels[0]);
4562                         Host_Error("Mod_Q3BSP_LoadLightmaps: invalid external lightmap size in %s",loadmodel->name);
4563                 }
4564
4565                 size = image_width;
4566                 bytesperpixel = 4;
4567                 rgbmap[0] = 0;
4568                 rgbmap[1] = 1;
4569                 rgbmap[2] = 2;
4570                 external = true;
4571
4572                 for(count = 1; ; ++count)
4573                 {
4574                         inpixels[count] = loadimagepixelsbgra(va("%s/lm_%04d", mapname, count), false, false, false);
4575                         if(!inpixels[count])
4576                                 break; // we got all of them
4577                         if(image_width != size || image_height != size)
4578                         {
4579                                 for(i = 0; i <= count; ++i)
4580                                         Mem_Free(inpixels[i]);
4581                                 Host_Error("Mod_Q3BSP_LoadLightmaps: invalid external lightmap size in %s",loadmodel->name);
4582                         }
4583                 }
4584         }
4585
4586         convertedpixels = (unsigned char *) Mem_Alloc(tempmempool, size*size*4);
4587         loadmodel->brushq3.lightmapsize = size;
4588         loadmodel->brushq3.num_originallightmaps = count;
4589
4590         // now check the surfaces to see if any of them index an odd numbered
4591         // lightmap, if so this is not a deluxemapped bsp file
4592         //
4593         // also check what lightmaps are actually used, because q3map2 sometimes
4594         // (always?) makes an unused one at the end, which
4595         // q3map2 sometimes (or always?) makes a second blank lightmap for no
4596         // reason when only one lightmap is used, which can throw off the
4597         // deluxemapping detection method, so check 2-lightmap bsp's specifically
4598         // to see if the second lightmap is blank, if so it is not deluxemapped.
4599         // VorteX: autodetect only if previous attempt to find "deluxeMaps" key
4600         // in Mod_Q3BSP_LoadEntities was failed
4601         if (!loadmodel->brushq3.deluxemapping)
4602         {
4603                 loadmodel->brushq3.deluxemapping = !(count & 1);
4604                 loadmodel->brushq3.deluxemapping_modelspace = true;
4605                 endlightmap = 0;
4606                 if (loadmodel->brushq3.deluxemapping)
4607                 {
4608                         int facecount = faceslump->filelen / sizeof(q3dface_t);
4609                         q3dface_t *faces = (q3dface_t *)(mod_base + faceslump->fileofs);
4610                         for (i = 0;i < facecount;i++)
4611                         {
4612                                 j = LittleLong(faces[i].lightmapindex);
4613                                 if (j >= 0)
4614                                 {
4615                                         endlightmap = max(endlightmap, j + 1);
4616                                         if ((j & 1) || j + 1 >= count)
4617                                         {
4618                                                 loadmodel->brushq3.deluxemapping = false;
4619                                                 break;
4620                                         }
4621                                 }
4622                         }
4623                 }
4624
4625                 // q3map2 sometimes (or always?) makes a second blank lightmap for no
4626                 // reason when only one lightmap is used, which can throw off the
4627                 // deluxemapping detection method, so check 2-lightmap bsp's specifically
4628                 // to see if the second lightmap is blank, if so it is not deluxemapped.
4629                 //
4630                 // further research has shown q3map2 sometimes creates a deluxemap and two
4631                 // blank lightmaps, which must be handled properly as well
4632                 if (endlightmap == 1 && count > 1)
4633                 {
4634                         c = inpixels[1];
4635                         for (i = 0;i < size*size;i++)
4636                         {
4637                                 if (c[bytesperpixel*i + rgbmap[0]])
4638                                         break;
4639                                 if (c[bytesperpixel*i + rgbmap[1]])
4640                                         break;
4641                                 if (c[bytesperpixel*i + rgbmap[2]])
4642                                         break;
4643                         }
4644                         if (i == size*size)
4645                         {
4646                                 // all pixels in the unused lightmap were black...
4647                                 loadmodel->brushq3.deluxemapping = false;
4648                         }
4649                 }
4650         }
4651
4652         Con_DPrintf("%s is %sdeluxemapped\n", loadmodel->name, loadmodel->brushq3.deluxemapping ? "" : "not ");
4653
4654         // figure out what the most reasonable merge power is within limits
4655
4656         loadmodel->brushq3.num_lightmapmergepower = 0;
4657
4658         for(i = 0; (128 << i) < size; ++i)
4659                 ;
4660         // i is now 0 for 128, 1 for 256, etc
4661
4662         for (power = 1;power + i <= mod_q3bsp_lightmapmergepower.integer && (size << power) <= (int)vid.maxtexturesize_2d && (1 << (power * 2)) < 4 * (count >> (loadmodel->brushq3.deluxemapping ? 1 : 0)); power++)
4663                 loadmodel->brushq3.num_lightmapmergepower = power;
4664
4665         loadmodel->brushq3.num_lightmapmerge = 1 << loadmodel->brushq3.num_lightmapmergepower;
4666
4667         loadmodel->brushq3.num_mergedlightmaps = ((count >> (loadmodel->brushq3.deluxemapping ? 1 : 0)) + (1 << (loadmodel->brushq3.num_lightmapmergepower * 2)) - 1) >> (loadmodel->brushq3.num_lightmapmergepower * 2);
4668         loadmodel->brushq3.data_lightmaps = (rtexture_t **)Mem_Alloc(loadmodel->mempool, loadmodel->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4669         if (loadmodel->brushq3.deluxemapping)
4670                 loadmodel->brushq3.data_deluxemaps = (rtexture_t **)Mem_Alloc(loadmodel->mempool, loadmodel->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
4671
4672         // allocate a texture pool if we need it
4673         if (loadmodel->texturepool == NULL && cls.state != ca_dedicated)
4674                 loadmodel->texturepool = R_AllocTexturePool();
4675
4676         power = loadmodel->brushq3.num_lightmapmergepower;
4677         power2 = power * 2;
4678         for (i = 0;i < count;i++)
4679         {
4680                 // figure out which merged lightmap texture this fits into
4681                 int lightmapindex = i >> (loadmodel->brushq3.deluxemapping + power2);
4682                 for (k = 0;k < size*size;k++)
4683                 {
4684                         convertedpixels[k*4+0] = inpixels[i][k*bytesperpixel+rgbmap[0]];
4685                         convertedpixels[k*4+1] = inpixels[i][k*bytesperpixel+rgbmap[1]];
4686                         convertedpixels[k*4+2] = inpixels[i][k*bytesperpixel+rgbmap[2]];
4687                         convertedpixels[k*4+3] = 255;
4688                 }
4689                 if (loadmodel->brushq3.num_lightmapmergepower > 0)
4690                 {
4691                         // if the lightmap has not been allocated yet, create it
4692                         if (!loadmodel->brushq3.data_lightmaps[lightmapindex])
4693                         {
4694                                 // create a lightmap only as large as necessary to hold the
4695                                 // remaining size*size blocks
4696                                 // if there are multiple merged lightmap textures then they will
4697                                 // all be full size except the last one which may be smaller
4698                                 // because it only needs to the remaining blocks, and it will often
4699                                 // be odd sizes like 2048x512 due to only being 25% full or so.
4700                                 j = (count >> (loadmodel->brushq3.deluxemapping ? 1 : 0)) - (lightmapindex << power2);
4701                                 for (mergewidth = 1;mergewidth < j && mergewidth < (1 << power);mergewidth *= 2)
4702                                         ;
4703                                 for (mergeheight = 1;mergewidth*mergeheight < j && mergeheight < (1 << power);mergeheight *= 2)
4704                                         ;
4705                                 if (developer_loading.integer)
4706                                         Con_Printf("lightmap merge texture #%i is %ix%i (%i of %i used)\n", lightmapindex, mergewidth*size, mergeheight*size, min(j, mergewidth*mergeheight), mergewidth*mergeheight);
4707                                 loadmodel->brushq3.data_lightmaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", lightmapindex), mergewidth * size, mergeheight * size, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | (gl_texturecompression_q3bsplightmaps.integer ? TEXF_COMPRESS : TEXF_ALLOWUPDATES), NULL);
4708                                 if (loadmodel->brushq3.data_deluxemaps)
4709                                         loadmodel->brushq3.data_deluxemaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%04i", lightmapindex), mergewidth * size, mergeheight * size, NULL, TEXTYPE_BGRA, TEXF_FORCELINEAR | (gl_texturecompression_q3bspdeluxemaps.integer ? TEXF_COMPRESS : TEXF_ALLOWUPDATES), NULL);
4710                         }
4711                         mergewidth = R_TextureWidth(loadmodel->brushq3.data_lightmaps[lightmapindex]) / size;
4712                         mergeheight = R_TextureHeight(loadmodel->brushq3.data_lightmaps[lightmapindex]) / size;
4713                         j = (i >> (loadmodel->brushq3.deluxemapping ? 1 : 0)) & ((1 << power2) - 1);
4714                         if (loadmodel->brushq3.deluxemapping && (i & 1))
4715                                 R_UpdateTexture(loadmodel->brushq3.data_deluxemaps[lightmapindex], convertedpixels, (j % mergewidth) * size, (j / mergewidth) * size, size, size);
4716                         else
4717                                 R_UpdateTexture(loadmodel->brushq3.data_lightmaps [lightmapindex], convertedpixels, (j % mergewidth) * size, (j / mergewidth) * size, size, size);
4718                 }
4719                 else
4720                 {
4721                         // figure out which merged lightmap texture this fits into
4722                         if (loadmodel->brushq3.deluxemapping && (i & 1))
4723                                 loadmodel->brushq3.data_deluxemaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%04i", lightmapindex), size, size, convertedpixels, TEXTYPE_BGRA, TEXF_FORCELINEAR | (gl_texturecompression_q3bspdeluxemaps.integer ? TEXF_COMPRESS : 0), NULL);
4724                         else
4725                                 loadmodel->brushq3.data_lightmaps [lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", lightmapindex), size, size, convertedpixels, TEXTYPE_BGRA, TEXF_FORCELINEAR | (gl_texturecompression_q3bsplightmaps.integer ? TEXF_COMPRESS : 0), NULL);
4726                 }
4727         }
4728
4729         Mem_Free(convertedpixels);
4730         if(external)
4731         {
4732                 for(i = 0; i < count; ++i)
4733                         Mem_Free(inpixels[i]);
4734         }
4735 }
4736
4737 static void Mod_Q3BSP_BuildBBoxes(const int *element3i, int num_triangles, const float *vertex3f, float **collisionbbox6f, int *collisionstride, int stride)
4738 {
4739         int j, k, cnt, tri;
4740         float *mins, *maxs;
4741         const float *vert;
4742         *collisionstride = stride;
4743         if(stride > 0)
4744         {
4745                 cnt = (num_triangles + stride - 1) / stride;
4746                 *collisionbbox6f = (float *) Mem_Alloc(loadmodel->mempool, sizeof(float[6]) * cnt);
4747                 for(j = 0; j < cnt; ++j)
4748                 {
4749                         mins = &((*collisionbbox6f)[6 * j + 0]);
4750                         maxs = &((*collisionbbox6f)[6 * j + 3]);
4751                         for(k = 0; k < stride; ++k)
4752                         {
4753                                 tri = j * stride + k;
4754                                 if(tri >= num_triangles)
4755                                         break;
4756                                 vert = &(vertex3f[element3i[3 * tri + 0] * 3]);
4757                                 if(!k || vert[0] < mins[0]) mins[0] = vert[0];
4758                                 if(!k || vert[1] < mins[1]) mins[1] = vert[1];
4759                                 if(!k || vert[2] < mins[2]) mins[2] = vert[2];
4760                                 if(!k || vert[0] > maxs[0]) maxs[0] = vert[0];
4761                                 if(!k || vert[1] > maxs[1]) maxs[1] = vert[1];
4762                                 if(!k || vert[2] > maxs[2]) maxs[2] = vert[2];
4763                                 vert = &(vertex3f[element3i[3 * tri + 1] * 3]);
4764                                 if(vert[0] < mins[0]) mins[0] = vert[0];
4765                                 if(vert[1] < mins[1]) mins[1] = vert[1];
4766                                 if(vert[2] < mins[2]) mins[2] = vert[2];
4767                                 if(vert[0] > maxs[0]) maxs[0] = vert[0];
4768                                 if(vert[1] > maxs[1]) maxs[1] = vert[1];
4769                                 if(vert[2] > maxs[2]) maxs[2] = vert[2];
4770                                 vert = &(vertex3f[element3i[3 * tri + 2] * 3]);
4771                                 if(vert[0] < mins[0]) mins[0] = vert[0];
4772                                 if(vert[1] < mins[1]) mins[1] = vert[1];
4773                                 if(vert[2] < mins[2]) mins[2] = vert[2];
4774                                 if(vert[0] > maxs[0]) maxs[0] = vert[0];
4775                                 if(vert[1] > maxs[1]) maxs[1] = vert[1];
4776                                 if(vert[2] > maxs[2]) maxs[2] = vert[2];
4777                         }
4778                 }
4779         }
4780         else
4781                 *collisionbbox6f = NULL;
4782 }
4783
4784 typedef struct patchtess_s
4785 {
4786         patchinfo_t info;
4787
4788         // Auxiliary data used only by patch loading code in Mod_Q3BSP_LoadFaces
4789         int surface_id;
4790         float lodgroup[6];
4791         float *originalvertex3f;
4792 } patchtess_t;
4793
4794 #define PATCHTESS_SAME_LODGROUP(a,b) \
4795         ( \
4796                 (a).lodgroup[0] == (b).lodgroup[0] && \
4797                 (a).lodgroup[1] == (b).lodgroup[1] && \
4798                 (a).lodgroup[2] == (b).lodgroup[2] && \
4799                 (a).lodgroup[3] == (b).lodgroup[3] && \
4800                 (a).lodgroup[4] == (b).lodgroup[4] && \
4801                 (a).lodgroup[5] == (b).lodgroup[5] \
4802         )
4803
4804 static void Mod_Q3BSP_LoadFaces(lump_t *l)
4805 {
4806         q3dface_t *in, *oldin;
4807         msurface_t *out, *oldout;
4808         int i, oldi, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xtess, ytess, finalvertices, finaltriangles, firstvertex, firstelement, type, oldnumtriangles, oldnumtriangles2, meshvertices, meshtriangles, collisionvertices, collisiontriangles, numvertices, numtriangles, cxtess, cytess;
4809         float lightmaptcbase[2], lightmaptcscale[2];
4810         //int *originalelement3i;
4811         //int *originalneighbor3i;
4812         float *originalvertex3f;
4813         //float *originalsvector3f;
4814         //float *originaltvector3f;
4815         float *originalnormal3f;
4816         float *originalcolor4f;
4817         float *originaltexcoordtexture2f;
4818         float *originaltexcoordlightmap2f;
4819         float *surfacecollisionvertex3f;
4820         int *surfacecollisionelement3i;
4821         float *v;
4822         patchtess_t *patchtess = NULL;
4823         int patchtesscount = 0;
4824         qboolean again;
4825
4826         in = (q3dface_t *)(mod_base + l->fileofs);
4827         if (l->filelen % sizeof(*in))
4828                 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
4829         count = l->filelen / sizeof(*in);
4830         out = (msurface_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4831
4832         loadmodel->data_surfaces = out;
4833         loadmodel->num_surfaces = count;
4834
4835         if(count > 0)
4836                 patchtess = (patchtess_t*) Mem_Alloc(tempmempool, count * sizeof(*patchtess));
4837
4838         i = 0;
4839         oldi = i;
4840         oldin = in;
4841         oldout = out;
4842         meshvertices = 0;
4843         meshtriangles = 0;
4844         for (;i < count;i++, in++, out++)
4845         {
4846                 // check face type first
4847                 type = LittleLong(in->type);
4848                 if (type != Q3FACETYPE_FLAT
4849                  && type != Q3FACETYPE_PATCH
4850                  && type != Q3FACETYPE_MESH
4851                  && type != Q3FACETYPE_FLARE)
4852                 {
4853                         Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, type);
4854                         continue;
4855                 }
4856
4857                 n = LittleLong(in->textureindex);
4858                 if (n < 0 || n >= loadmodel->num_textures)
4859                 {
4860                         Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->num_textures);
4861                         continue;
4862                 }
4863                 out->texture = loadmodel->data_textures + n;
4864                 n = LittleLong(in->effectindex);
4865                 if (n < -1 || n >= loadmodel->brushq3.num_effects)
4866                 {
4867                         if (developer_extra.integer)
4868                                 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
4869                         n = -1;
4870                 }
4871                 if (n == -1)
4872                         out->effect = NULL;
4873                 else
4874                         out->effect = loadmodel->brushq3.data_effects + n;
4875
4876                 if (cls.state != ca_dedicated)
4877                 {
4878                         out->lightmaptexture = NULL;
4879                         out->deluxemaptexture = r_texture_blanknormalmap;
4880                         n = LittleLong(in->lightmapindex);
4881                         if (n < 0)
4882                                 n = -1;
4883                         else if (n >= loadmodel->brushq3.num_originallightmaps)
4884                         {
4885                                 if(loadmodel->brushq3.num_originallightmaps != 0)
4886                                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_originallightmaps);
4887                                 n = -1;
4888                         }
4889                         else
4890                         {
4891                                 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n >> (loadmodel->brushq3.num_lightmapmergepower * 2 + loadmodel->brushq3.deluxemapping)];
4892                                 if (loadmodel->brushq3.deluxemapping)
4893                                         out->deluxemaptexture = loadmodel->brushq3.data_deluxemaps[n >> (loadmodel->brushq3.num_lightmapmergepower * 2 + loadmodel->brushq3.deluxemapping)];
4894                         }
4895                 }
4896
4897                 firstvertex = LittleLong(in->firstvertex);
4898                 numvertices = LittleLong(in->numvertices);
4899                 firstelement = LittleLong(in->firstelement);
4900                 numtriangles = LittleLong(in->numelements) / 3;
4901                 if (numtriangles * 3 != LittleLong(in->numelements))
4902                 {
4903                         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));
4904                         continue;
4905                 }
4906                 if (firstvertex < 0 || firstvertex + numvertices > loadmodel->brushq3.num_vertices)
4907                 {
4908                         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);
4909                         continue;
4910                 }
4911                 if (firstelement < 0 || firstelement + numtriangles * 3 > loadmodel->brushq3.num_triangles * 3)
4912                 {
4913                         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);
4914                         continue;
4915                 }
4916                 switch(type)
4917                 {
4918                 case Q3FACETYPE_FLAT:
4919                 case Q3FACETYPE_MESH:
4920                         // no processing necessary
4921                         break;
4922                 case Q3FACETYPE_PATCH:
4923                         patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
4924                         patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
4925                         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))
4926                         {
4927                                 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
4928                                 continue;
4929                         }
4930                         originalvertex3f = loadmodel->brushq3.data_vertex3f + firstvertex * 3;
4931
4932                         // convert patch to Q3FACETYPE_MESH
4933                         xtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
4934                         ytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value);
4935                         // bound to user settings
4936                         xtess = bound(r_subdivisions_mintess.integer, xtess, r_subdivisions_maxtess.integer);
4937                         ytess = bound(r_subdivisions_mintess.integer, ytess, r_subdivisions_maxtess.integer);
4938                         // bound to sanity settings
4939                         xtess = bound(0, xtess, 1024);
4940                         ytess = bound(0, ytess, 1024);
4941
4942                         // lower quality collision patches! Same procedure as before, but different cvars
4943                         // convert patch to Q3FACETYPE_MESH
4944                         cxtess = Q3PatchTesselationOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value);
4945                         cytess = Q3PatchTesselationOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value);
4946                         // bound to user settings
4947                         cxtess = bound(r_subdivisions_collision_mintess.integer, cxtess, r_subdivisions_collision_maxtess.integer);
4948                         cytess = bound(r_subdivisions_collision_mintess.integer, cytess, r_subdivisions_collision_maxtess.integer);
4949                         // bound to sanity settings
4950                         cxtess = bound(0, cxtess, 1024);
4951                         cytess = bound(0, cytess, 1024);
4952
4953                         // store it for the LOD grouping step
4954                         patchtess[patchtesscount].info.xsize = patchsize[0];
4955                         patchtess[patchtesscount].info.ysize = patchsize[1];
4956                         patchtess[patchtesscount].info.lods[PATCH_LOD_VISUAL].xtess = xtess;
4957                         patchtess[patchtesscount].info.lods[PATCH_LOD_VISUAL].ytess = ytess;
4958                         patchtess[patchtesscount].info.lods[PATCH_LOD_COLLISION].xtess = cxtess;
4959                         patchtess[patchtesscount].info.lods[PATCH_LOD_COLLISION].ytess = cytess;
4960         
4961                         patchtess[patchtesscount].surface_id = i;
4962                         patchtess[patchtesscount].lodgroup[0] = LittleFloat(in->specific.patch.mins[0]);
4963                         patchtess[patchtesscount].lodgroup[1] = LittleFloat(in->specific.patch.mins[1]);
4964                         patchtess[patchtesscount].lodgroup[2] = LittleFloat(in->specific.patch.mins[2]);
4965                         patchtess[patchtesscount].lodgroup[3] = LittleFloat(in->specific.patch.maxs[0]);
4966                         patchtess[patchtesscount].lodgroup[4] = LittleFloat(in->specific.patch.maxs[1]);
4967                         patchtess[patchtesscount].lodgroup[5] = LittleFloat(in->specific.patch.maxs[2]);
4968                         patchtess[patchtesscount].originalvertex3f = originalvertex3f;
4969                         ++patchtesscount;
4970                         break;
4971                 case Q3FACETYPE_FLARE:
4972                         if (developer_extra.integer)
4973                                 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
4974                         // don't render it
4975                         continue;
4976                 }
4977                 out->num_vertices = numvertices;
4978                 out->num_triangles = numtriangles;
4979                 meshvertices += out->num_vertices;
4980                 meshtriangles += out->num_triangles;
4981         }
4982
4983         // Fix patches tesselations so that they make no seams
4984         do
4985         {
4986                 again = false;
4987                 for(i = 0; i < patchtesscount; ++i)
4988                 {
4989                         for(j = i+1; j < patchtesscount; ++j)
4990                         {
4991                                 if (!PATCHTESS_SAME_LODGROUP(patchtess[i], patchtess[j]))
4992                                         continue;
4993
4994                                 if (Q3PatchAdjustTesselation(3, &patchtess[i].info, patchtess[i].originalvertex3f, &patchtess[j].info, patchtess[j].originalvertex3f) )
4995                                         again = true;
4996                         }
4997                 }
4998         }
4999         while (again);
5000
5001         // Calculate resulting number of triangles
5002         collisionvertices = 0;
5003         collisiontriangles = 0;
5004         for(i = 0; i < patchtesscount; ++i)
5005         {
5006                 finalwidth = Q3PatchDimForTess(patchtess[i].info.xsize, patchtess[i].info.lods[PATCH_LOD_VISUAL].xtess);
5007                 finalheight = Q3PatchDimForTess(patchtess[i].info.ysize,patchtess[i].info.lods[PATCH_LOD_VISUAL].ytess);
5008                 numvertices = finalwidth * finalheight;
5009                 numtriangles = (finalwidth - 1) * (finalheight - 1) * 2;
5010
5011                 oldout[patchtess[i].surface_id].num_vertices = numvertices;
5012                 oldout[patchtess[i].surface_id].num_triangles = numtriangles;
5013                 meshvertices += oldout[patchtess[i].surface_id].num_vertices;
5014                 meshtriangles += oldout[patchtess[i].surface_id].num_triangles;
5015
5016                 finalwidth = Q3PatchDimForTess(patchtess[i].info.xsize, patchtess[i].info.lods[PATCH_LOD_COLLISION].xtess);
5017                 finalheight = Q3PatchDimForTess(patchtess[i].info.ysize,patchtess[i].info.lods[PATCH_LOD_COLLISION].ytess);
5018                 numvertices = finalwidth * finalheight;
5019                 numtriangles = (finalwidth - 1) * (finalheight - 1) * 2;
5020
5021                 oldout[patchtess[i].surface_id].num_collisionvertices = numvertices;
5022                 oldout[patchtess[i].surface_id].num_collisiontriangles = numtriangles;
5023                 collisionvertices += oldout[patchtess[i].surface_id].num_collisionvertices;
5024                 collisiontriangles += oldout[patchtess[i].surface_id].num_collisiontriangles;
5025         }
5026
5027         i = oldi;
5028         in = oldin;
5029         out = oldout;
5030         Mod_AllocSurfMesh(loadmodel->mempool, meshvertices, meshtriangles, false, true, false);
5031         if (collisiontriangles)
5032         {
5033                 loadmodel->brush.data_collisionvertex3f = Mem_Alloc(loadmodel->mempool, collisionvertices * sizeof(float[3]));
5034                 loadmodel->brush.data_collisionelement3i = Mem_Alloc(loadmodel->mempool, collisiontriangles * sizeof(int[3]));
5035         }
5036         meshvertices = 0;
5037         meshtriangles = 0;
5038         collisionvertices = 0;
5039         collisiontriangles = 0;
5040         for (;i < count && meshvertices + out->num_vertices <= loadmodel->surfmesh.num_vertices;i++, in++, out++)
5041         {
5042                 if (out->num_vertices < 3 || out->num_triangles < 1)
5043                         continue;
5044
5045                 type = LittleLong(in->type);
5046                 firstvertex = LittleLong(in->firstvertex);
5047                 firstelement = LittleLong(in->firstelement);
5048                 out->num_firstvertex = meshvertices;
5049                 out->num_firsttriangle = meshtriangles;
5050                 out->num_firstcollisiontriangle = collisiontriangles;
5051                 switch(type)
5052                 {
5053                 case Q3FACETYPE_FLAT:
5054                 case Q3FACETYPE_MESH:
5055                         // no processing necessary, except for lightmap merging
5056                         for (j = 0;j < out->num_vertices;j++)
5057                         {
5058                                 (loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex)[j * 3 + 0] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 0];
5059                                 (loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex)[j * 3 + 1] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 1];
5060                                 (loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex)[j * 3 + 2] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 2];
5061                                 (loadmodel->surfmesh.data_normal3f + 3 * out->num_firstvertex)[j * 3 + 0] = loadmodel->brushq3.data_normal3f[(firstvertex + j) * 3 + 0];
5062                                 (loadmodel->surfmesh.data_normal3f + 3 * out->num_firstvertex)[j * 3 + 1] = loadmodel->brushq3.data_normal3f[(firstvertex + j) * 3 + 1];
5063                                 (loadmodel->surfmesh.data_normal3f + 3 * out->num_firstvertex)[j * 3 + 2] = loadmodel->brushq3.data_normal3f[(firstvertex + j) * 3 + 2];
5064                                 (loadmodel->surfmesh.data_texcoordtexture2f + 2 * out->num_firstvertex)[j * 2 + 0] = loadmodel->brushq3.data_texcoordtexture2f[(firstvertex + j) * 2 + 0];
5065                                 (loadmodel->surfmesh.data_texcoordtexture2f + 2 * out->num_firstvertex)[j * 2 + 1] = loadmodel->brushq3.data_texcoordtexture2f[(firstvertex + j) * 2 + 1];
5066                                 (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * out->num_firstvertex)[j * 2 + 0] = loadmodel->brushq3.data_texcoordlightmap2f[(firstvertex + j) * 2 + 0];
5067                                 (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * out->num_firstvertex)[j * 2 + 1] = loadmodel->brushq3.data_texcoordlightmap2f[(firstvertex + j) * 2 + 1];
5068                                 (loadmodel->surfmesh.data_lightmapcolor4f + 4 * out->num_firstvertex)[j * 4 + 0] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 0];
5069                                 (loadmodel->surfmesh.data_lightmapcolor4f + 4 * out->num_firstvertex)[j * 4 + 1] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 1];
5070                                 (loadmodel->surfmesh.data_lightmapcolor4f + 4 * out->num_firstvertex)[j * 4 + 2] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 2];
5071                                 (loadmodel->surfmesh.data_lightmapcolor4f + 4 * out->num_firstvertex)[j * 4 + 3] = loadmodel->brushq3.data_color4f[(firstvertex + j) * 4 + 3];
5072                         }
5073                         for (j = 0;j < out->num_triangles*3;j++)
5074                                 (loadmodel->surfmesh.data_element3i + 3 * out->num_firsttriangle)[j] = loadmodel->brushq3.data_element3i[firstelement + j] + out->num_firstvertex;
5075                         break;
5076                 case Q3FACETYPE_PATCH:
5077                         patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
5078                         patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
5079                         originalvertex3f = loadmodel->brushq3.data_vertex3f + firstvertex * 3;
5080                         originalnormal3f = loadmodel->brushq3.data_normal3f + firstvertex * 3;
5081                         originaltexcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + firstvertex * 2;
5082                         originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + firstvertex * 2;
5083                         originalcolor4f = loadmodel->brushq3.data_color4f + firstvertex * 4;
5084
5085                         xtess = ytess = cxtess = cytess = -1;
5086                         for(j = 0; j < patchtesscount; ++j)
5087                                 if(patchtess[j].surface_id == i)
5088                                 {
5089                                         xtess = patchtess[j].info.lods[PATCH_LOD_VISUAL].xtess;
5090                                         ytess = patchtess[j].info.lods[PATCH_LOD_VISUAL].ytess;
5091                                         cxtess = patchtess[j].info.lods[PATCH_LOD_COLLISION].xtess;
5092                                         cytess = patchtess[j].info.lods[PATCH_LOD_COLLISION].ytess;
5093                                         break;
5094                                 }
5095                         if(xtess == -1)
5096                         {
5097                                 Con_Printf("ERROR: patch %d isn't preprocessed?!?\n", i);
5098                                 xtess = ytess = cxtess = cytess = 0;
5099                         }
5100
5101                         finalwidth = Q3PatchDimForTess(patchsize[0],xtess); //((patchsize[0] - 1) * xtess) + 1;
5102                         finalheight = Q3PatchDimForTess(patchsize[1],ytess); //((patchsize[1] - 1) * ytess) + 1;
5103                         finalvertices = finalwidth * finalheight;
5104                         finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
5105                         type = Q3FACETYPE_MESH;
5106                         // generate geometry
5107                         // (note: normals are skipped because they get recalculated)
5108                         Q3PatchTesselateFloat(3, sizeof(float[3]), (loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[3]), originalvertex3f, xtess, ytess);
5109                         Q3PatchTesselateFloat(3, sizeof(float[3]), (loadmodel->surfmesh.data_normal3f + 3 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[3]), originalnormal3f, xtess, ytess);
5110                         Q3PatchTesselateFloat(2, sizeof(float[2]), (loadmodel->surfmesh.data_texcoordtexture2f + 2 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[2]), originaltexcoordtexture2f, xtess, ytess);
5111                         Q3PatchTesselateFloat(2, sizeof(float[2]), (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[2]), originaltexcoordlightmap2f, xtess, ytess);
5112                         Q3PatchTesselateFloat(4, sizeof(float[4]), (loadmodel->surfmesh.data_lightmapcolor4f + 4 * out->num_firstvertex), patchsize[0], patchsize[1], sizeof(float[4]), originalcolor4f, xtess, ytess);
5113                         Q3PatchTriangleElements((loadmodel->surfmesh.data_element3i + 3 * out->num_firsttriangle), finalwidth, finalheight, out->num_firstvertex);
5114
5115                         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);
5116
5117                         if (developer_extra.integer)
5118                         {
5119                                 if (out->num_triangles < finaltriangles)
5120                                         Con_DPrintf("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);
5121                                 else
5122                                         Con_DPrintf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles\n", patchsize[0], patchsize[1], out->num_vertices, out->num_triangles);
5123                         }
5124                         // q3map does not put in collision brushes for curves... ugh
5125                         // build the lower quality collision geometry
5126                         finalwidth = Q3PatchDimForTess(patchsize[0],cxtess); //((patchsize[0] - 1) * cxtess) + 1;
5127                         finalheight = Q3PatchDimForTess(patchsize[1],cytess); //((patchsize[1] - 1) * cytess) + 1;
5128                         finalvertices = finalwidth * finalheight;
5129                         finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
5130
5131                         // legacy collision geometry implementation
5132                         out->deprecatedq3data_collisionvertex3f = (float *)Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * finalvertices);
5133                         out->deprecatedq3data_collisionelement3i = (int *)Mem_Alloc(loadmodel->mempool, sizeof(int[3]) * finaltriangles);
5134                         out->num_collisionvertices = finalvertices;
5135                         out->num_collisiontriangles = finaltriangles;
5136                         Q3PatchTesselateFloat(3, sizeof(float[3]), out->deprecatedq3data_collisionvertex3f, patchsize[0], patchsize[1], sizeof(float[3]), originalvertex3f, cxtess, cytess);
5137                         Q3PatchTriangleElements(out->deprecatedq3data_collisionelement3i, finalwidth, finalheight, 0);
5138
5139                         //Mod_SnapVertices(3, out->num_vertices, (loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex), 0.25);
5140                         Mod_SnapVertices(3, out->num_collisionvertices, out->deprecatedq3data_collisionvertex3f, 1);
5141
5142                         oldnumtriangles = out->num_triangles;
5143                         oldnumtriangles2 = out->num_collisiontriangles;
5144                         out->num_collisiontriangles = Mod_RemoveDegenerateTriangles(out->num_collisiontriangles, out->deprecatedq3data_collisionelement3i, out->deprecatedq3data_collisionelement3i, out->deprecatedq3data_collisionvertex3f);
5145
5146                         // now optimize the collision mesh by finding triangle bboxes...
5147                         Mod_Q3BSP_BuildBBoxes(out->deprecatedq3data_collisionelement3i, out->num_collisiontriangles, out->deprecatedq3data_collisionvertex3f, &out->deprecatedq3data_collisionbbox6f, &out->deprecatedq3num_collisionbboxstride, mod_q3bsp_curves_collisions_stride.integer);
5148                         Mod_Q3BSP_BuildBBoxes(loadmodel->surfmesh.data_element3i + 3 * out->num_firsttriangle, out->num_triangles, loadmodel->surfmesh.data_vertex3f, &out->deprecatedq3data_bbox6f, &out->deprecatedq3num_bboxstride, mod_q3bsp_curves_stride.integer);
5149
5150                         // store collision geometry for BIH collision tree
5151                         surfacecollisionvertex3f = loadmodel->brush.data_collisionvertex3f + collisionvertices * 3;
5152                         surfacecollisionelement3i = loadmodel->brush.data_collisionelement3i + collisiontriangles * 3;
5153                         Q3PatchTesselateFloat(3, sizeof(float[3]), surfacecollisionvertex3f, patchsize[0], patchsize[1], sizeof(float[3]), originalvertex3f, cxtess, cytess);
5154                         Q3PatchTriangleElements(surfacecollisionelement3i, finalwidth, finalheight, collisionvertices);
5155                         Mod_SnapVertices(3, finalvertices, surfacecollisionvertex3f, 1);
5156                         oldnumtriangles = out->num_triangles;
5157                         oldnumtriangles2 = out->num_collisiontriangles;
5158                         out->num_collisiontriangles = Mod_RemoveDegenerateTriangles(out->num_collisiontriangles, surfacecollisionelement3i, surfacecollisionelement3i, loadmodel->brush.data_collisionvertex3f);
5159
5160                         if (developer_extra.integer)
5161                                 Con_DPrintf("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);
5162
5163                         collisionvertices += finalvertices;
5164                         collisiontriangles += out->num_collisiontriangles;
5165                         break;
5166                 default:
5167                         break;
5168                 }
5169                 meshvertices += out->num_vertices;
5170                 meshtriangles += out->num_triangles;
5171                 for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++)
5172                         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)
5173                                 invalidelements++;
5174                 if (invalidelements)
5175                 {
5176                         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);
5177                         for (j = 0;j < out->num_triangles * 3;j++)
5178                         {
5179                                 Con_Printf(" %i", (loadmodel->surfmesh.data_element3i + 3 * out->num_firsttriangle)[j] - out->num_firstvertex);
5180                                 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)
5181                                         (loadmodel->surfmesh.data_element3i + 3 * out->num_firsttriangle)[j] = out->num_firstvertex;
5182                         }
5183                         Con_Print("\n");
5184                 }
5185                 // calculate a bounding box
5186                 VectorClear(out->mins);
5187                 VectorClear(out->maxs);
5188                 if (out->num_vertices)
5189                 {
5190                         if (cls.state != ca_dedicated && out->lightmaptexture)
5191                         {
5192                                 // figure out which part of the merged lightmap this fits into
5193                                 int lightmapindex = LittleLong(in->lightmapindex) >> (loadmodel->brushq3.deluxemapping ? 1 : 0);
5194                                 int mergewidth = R_TextureWidth(out->lightmaptexture) / loadmodel->brushq3.lightmapsize;
5195                                 int mergeheight = R_TextureHeight(out->lightmaptexture) / loadmodel->brushq3.lightmapsize;
5196                                 lightmapindex &= mergewidth * mergeheight - 1;
5197                                 lightmaptcscale[0] = 1.0f / mergewidth;
5198                                 lightmaptcscale[1] = 1.0f / mergeheight;
5199                                 lightmaptcbase[0] = (lightmapindex % mergewidth) * lightmaptcscale[0];
5200                                 lightmaptcbase[1] = (lightmapindex / mergewidth) * lightmaptcscale[1];
5201                                 // modify the lightmap texcoords to match this region of the merged lightmap
5202                                 for (j = 0, v = loadmodel->surfmesh.data_texcoordlightmap2f + 2 * out->num_firstvertex;j < out->num_vertices;j++, v += 2)
5203                                 {
5204                                         v[0] = v[0] * lightmaptcscale[0] + lightmaptcbase[0];
5205                                         v[1] = v[1] * lightmaptcscale[1] + lightmaptcbase[1];
5206                                 }
5207                         }
5208                         VectorCopy((loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex), out->mins);
5209                         VectorCopy((loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex), out->maxs);
5210                         for (j = 1, v = (loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex) + 3;j < out->num_vertices;j++, v += 3)
5211                         {
5212                                 out->mins[0] = min(out->mins[0], v[0]);
5213                                 out->maxs[0] = max(out->maxs[0], v[0]);
5214                                 out->mins[1] = min(out->mins[1], v[1]);
5215                                 out->maxs[1] = max(out->maxs[1], v[1]);
5216                                 out->mins[2] = min(out->mins[2], v[2]);
5217                                 out->maxs[2] = max(out->maxs[2], v[2]);
5218                         }
5219                         out->mins[0] -= 1.0f;
5220                         out->mins[1] -= 1.0f;
5221                         out->mins[2] -= 1.0f;
5222                         out->maxs[0] += 1.0f;
5223                         out->maxs[1] += 1.0f;
5224                         out->maxs[2] += 1.0f;
5225                 }
5226                 // set lightmap styles for consistency with q1bsp
5227                 //out->lightmapinfo->styles[0] = 0;
5228                 //out->lightmapinfo->styles[1] = 255;
5229                 //out->lightmapinfo->styles[2] = 255;
5230                 //out->lightmapinfo->styles[3] = 255;
5231         }
5232
5233         i = oldi;
5234         out = oldout;
5235         for (;i < count;i++, out++)
5236         {
5237                 if(out->num_vertices && out->num_triangles)
5238                         continue;
5239                 if(out->num_vertices == 0)
5240                         Con_Printf("Mod_Q3BSP_LoadFaces: surface %d has no vertices, ignoring\n", i);
5241                 if(out->num_triangles == 0)
5242                         Con_Printf("Mod_Q3BSP_LoadFaces: surface %d has no triangles, ignoring\n", i);
5243         }
5244
5245         // for per pixel lighting
5246         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);
5247
5248         // generate ushort elements array if possible
5249         if (loadmodel->surfmesh.data_element3s)
5250                 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
5251                         loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
5252
5253         // free the no longer needed vertex data
5254         loadmodel->brushq3.num_vertices = 0;
5255         if (loadmodel->brushq3.data_vertex3f)
5256                 Mem_Free(loadmodel->brushq3.data_vertex3f);
5257         loadmodel->brushq3.data_vertex3f = NULL;
5258         loadmodel->brushq3.data_normal3f = NULL;
5259         loadmodel->brushq3.data_texcoordtexture2f = NULL;
5260         loadmodel->brushq3.data_texcoordlightmap2f = NULL;
5261         loadmodel->brushq3.data_color4f = NULL;
5262         // free the no longer needed triangle data
5263         loadmodel->brushq3.num_triangles = 0;
5264         if (loadmodel->brushq3.data_element3i)
5265                 Mem_Free(loadmodel->brushq3.data_element3i);
5266         loadmodel->brushq3.data_element3i = NULL;
5267
5268         if(patchtess)
5269                 Mem_Free(patchtess);
5270 }
5271
5272 static void Mod_Q3BSP_LoadModels(lump_t *l)
5273 {
5274         q3dmodel_t *in;
5275         q3dmodel_t *out;
5276         int i, j, n, c, count;
5277
5278         in = (q3dmodel_t *)(mod_base + l->fileofs);
5279         if (l->filelen % sizeof(*in))
5280                 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
5281         count = l->filelen / sizeof(*in);
5282         out = (q3dmodel_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
5283
5284         loadmodel->brushq3.data_models = out;
5285         loadmodel->brushq3.num_models = count;
5286
5287         for (i = 0;i < count;i++, in++, out++)
5288         {
5289                 for (j = 0;j < 3;j++)
5290                 {
5291                         out->mins[j] = LittleFloat(in->mins[j]);
5292                         out->maxs[j] = LittleFloat(in->maxs[j]);
5293                 }
5294                 n = LittleLong(in->firstface);
5295                 c = LittleLong(in->numfaces);
5296                 if (n < 0 || n + c > loadmodel->num_surfaces)
5297                         Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)", n, n + c, loadmodel->num_surfaces);
5298                 out->firstface = n;
5299                 out->numfaces = c;
5300                 n = LittleLong(in->firstbrush);
5301                 c = LittleLong(in->numbrushes);
5302                 if (n < 0 || n + c > loadmodel->brush.num_brushes)
5303                         Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)", n, n + c, loadmodel->brush.num_brushes);
5304                 out->firstbrush = n;
5305                 out->numbrushes = c;
5306         }
5307 }
5308
5309 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
5310 {
5311         int *in;
5312         int *out;
5313         int i, n, count;
5314
5315         in = (int *)(mod_base + l->fileofs);
5316         if (l->filelen % sizeof(*in))
5317                 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
5318         count = l->filelen / sizeof(*in);
5319         out = (int *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
5320
5321         loadmodel->brush.data_leafbrushes = out;
5322         loadmodel->brush.num_leafbrushes = count;
5323
5324         for (i = 0;i < count;i++, in++, out++)
5325         {
5326                 n = LittleLong(*in);
5327                 if (n < 0 || n >= loadmodel->brush.num_brushes)
5328                         Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)", n, loadmodel->brush.num_brushes);
5329                 *out = n;
5330         }
5331 }
5332
5333 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
5334 {
5335         int *in;
5336         int *out;
5337         int i, n, count;
5338
5339         in = (int *)(mod_base + l->fileofs);
5340         if (l->filelen % sizeof(*in))
5341                 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
5342         count = l->filelen / sizeof(*in);
5343         out = (int *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
5344
5345         loadmodel->brush.data_leafsurfaces = out;
5346         loadmodel->brush.num_leafsurfaces = count;
5347
5348         for (i = 0;i < count;i++, in++, out++)
5349         {
5350                 n = LittleLong(*in);
5351                 if (n < 0 || n >= loadmodel->num_surfaces)
5352                         Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)", n, loadmodel->num_surfaces);
5353                 *out = n;
5354         }
5355 }
5356
5357 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
5358 {
5359         q3dleaf_t *in;
5360         mleaf_t *out;
5361         int i, j, n, c, count;
5362
5363         in = (q3dleaf_t *)(mod_base + l->fileofs);
5364         if (l->filelen % sizeof(*in))
5365                 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
5366         count = l->filelen / sizeof(*in);
5367         out = (mleaf_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
5368
5369         loadmodel->brush.data_leafs = out;
5370         loadmodel->brush.num_leafs = count;
5371
5372         for (i = 0;i < count;i++, in++, out++)
5373         {
5374                 out->parent = NULL;
5375                 out->plane = NULL;
5376                 out->clusterindex = LittleLong(in->clusterindex);
5377                 out->areaindex = LittleLong(in->areaindex);
5378                 for (j = 0;j < 3;j++)
5379                 {
5380                         // yes the mins/maxs are ints
5381                         out->mins[j] = LittleLong(in->mins[j]) - 1;
5382                         out->maxs[j] = LittleLong(in->maxs[j]) + 1;
5383                 }
5384                 n = LittleLong(in->firstleafface);
5385                 c = LittleLong(in->numleaffaces);
5386                 if (n < 0 || n + c > loadmodel->brush.num_leafsurfaces)
5387                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafsurface range %i : %i (%i leafsurfaces)", n, n + c, loadmodel->brush.num_leafsurfaces);
5388                 out->firstleafsurface = loadmodel->brush.data_leafsurfaces + n;
5389                 out->numleafsurfaces = c;
5390                 n = LittleLong(in->firstleafbrush);
5391                 c = LittleLong(in->numleafbrushes);
5392                 if (n < 0 || n + c > loadmodel->brush.num_leafbrushes)
5393                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)", n, n + c, loadmodel->brush.num_leafbrushes);
5394                 out->firstleafbrush = loadmodel->brush.data_leafbrushes + n;
5395                 out->numleafbrushes = c;
5396         }
5397 }
5398
5399 static void Mod_Q3BSP_LoadNodes(lump_t *l)
5400 {
5401         q3dnode_t *in;
5402         mnode_t *out;
5403         int i, j, n, count;
5404
5405         in = (q3dnode_t *)(mod_base + l->fileofs);
5406         if (l->filelen % sizeof(*in))
5407                 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
5408         count = l->filelen / sizeof(*in);
5409         out = (mnode_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
5410
5411         loadmodel->brush.data_nodes = out;
5412         loadmodel->brush.num_nodes = count;
5413
5414         for (i = 0;i < count;i++, in++, out++)
5415         {
5416                 out->parent = NULL;
5417                 n = LittleLong(in->planeindex);
5418                 if (n < 0 || n >= loadmodel->brush.num_planes)
5419                         Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)", n, loadmodel->brush.num_planes);
5420                 out->plane = loadmodel->brush.data_planes + n;
5421                 for (j = 0;j < 2;j++)
5422                 {
5423                         n = LittleLong(in->childrenindex[j]);
5424                         if (n >= 0)
5425                         {
5426                                 if (n >= loadmodel->brush.num_nodes)
5427                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)", n, loadmodel->brush.num_nodes);
5428                                 out->children[j] = loadmodel->brush.data_nodes + n;
5429                         }
5430                         else
5431                         {
5432                                 n = -1 - n;
5433                                 if (n >= loadmodel->brush.num_leafs)
5434                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)", n, loadmodel->brush.num_leafs);
5435                                 out->children[j] = (mnode_t *)(loadmodel->brush.data_leafs + n);
5436                         }
5437                 }
5438                 for (j = 0;j < 3;j++)
5439                 {
5440                         // yes the mins/maxs are ints
5441                         out->mins[j] = LittleLong(in->mins[j]) - 1;
5442                         out->maxs[j] = LittleLong(in->maxs[j]) + 1;
5443                 }
5444         }
5445
5446         // set the parent pointers
5447         Mod_Q1BSP_LoadNodes_RecursiveSetParent(loadmodel->brush.data_nodes, NULL);
5448 }
5449
5450 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
5451 {
5452         q3dlightgrid_t *in;
5453         q3dlightgrid_t *out;
5454         int count;
5455
5456         in = (q3dlightgrid_t *)(mod_base + l->fileofs);
5457         if (l->filelen % sizeof(*in))
5458                 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
5459         loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
5460         loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
5461         loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
5462         loadmodel->brushq3.num_lightgrid_imins[0] = (int)ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
5463         loadmodel->brushq3.num_lightgrid_imins[1] = (int)ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
5464         loadmodel->brushq3.num_lightgrid_imins[2] = (int)ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
5465         loadmodel->brushq3.num_lightgrid_imaxs[0] = (int)floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
5466         loadmodel->brushq3.num_lightgrid_imaxs[1] = (int)floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
5467         loadmodel->brushq3.num_lightgrid_imaxs[2] = (int)floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
5468         loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
5469         loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
5470         loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
5471         count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
5472         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]);
5473         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]);
5474
5475         // if lump is empty there is nothing to load, we can deal with that in the LightPoint code
5476         if (l->filelen)
5477         {
5478                 if (l->filelen < count * (int)sizeof(*in))
5479                 {
5480                         Con_Printf("Mod_Q3BSP_LoadLightGrid: invalid lightgrid lump size %i bytes, should be %i bytes (%ix%ix%i)", l->filelen, (int)(count * sizeof(*in)), loadmodel->brushq3.num_lightgrid_isize[0], loadmodel->brushq3.num_lightgrid_isize[1], loadmodel->brushq3.num_lightgrid_isize[2]);
5481                         return; // ignore the grid if we cannot understand it
5482                 }
5483                 if (l->filelen != count * (int)sizeof(*in))
5484                         Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", (int)(count * sizeof(*in)), l->filelen);
5485                 out = (q3dlightgrid_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
5486                 loadmodel->brushq3.data_lightgrid = out;
5487                 loadmodel->brushq3.num_lightgrid = count;
5488                 // no swapping or validation necessary
5489                 memcpy(out, in, count * (int)sizeof(*out));
5490         }
5491 }
5492
5493 static void Mod_Q3BSP_LoadPVS(lump_t *l)
5494 {
5495         q3dpvs_t *in;
5496         int totalchains;
5497
5498         if (l->filelen == 0)
5499         {
5500                 int i;
5501                 // unvised maps often have cluster indices even without pvs, so check
5502                 // leafs to find real number of clusters
5503                 loadmodel->brush.num_pvsclusters = 1;
5504                 for (i = 0;i < loadmodel->brush.num_leafs;i++)
5505                         loadmodel->brush.num_pvsclusters = max(loadmodel->brush.num_pvsclusters, loadmodel->brush.data_leafs[i].clusterindex + 1);
5506
5507                 // create clusters
5508                 loadmodel->brush.num_pvsclusterbytes = (loadmodel->brush.num_pvsclusters + 7) / 8;
5509                 totalchains = loadmodel->brush.num_pvsclusterbytes * loadmodel->brush.num_pvsclusters;
5510                 loadmodel->brush.data_pvsclusters = (unsigned char *)Mem_Alloc(loadmodel->mempool, totalchains);
5511                 memset(loadmodel->brush.data_pvsclusters, 0xFF, totalchains);
5512                 return;
5513         }
5514
5515         in = (q3dpvs_t *)(mod_base + l->fileofs);
5516         if (l->filelen < 9)
5517                 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
5518
5519         loadmodel->brush.num_pvsclusters = LittleLong(in->numclusters);
5520         loadmodel->brush.num_pvsclusterbytes = LittleLong(in->chainlength);
5521         if (loadmodel->brush.num_pvsclusterbytes < ((loadmodel->brush.num_pvsclusters + 7) / 8))
5522                 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8", loadmodel->brush.num_pvsclusterbytes, loadmodel->brush.num_pvsclusters);
5523         totalchains = loadmodel->brush.num_pvsclusterbytes * loadmodel->brush.num_pvsclusters;
5524         if (l->filelen < totalchains + (int)sizeof(*in))
5525                 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, (int)(totalchains + sizeof(*in)), l->filelen);
5526
5527         loadmodel->brush.data_pvsclusters = (unsigned char *)Mem_Alloc(loadmodel->mempool, totalchains);
5528         memcpy(loadmodel->brush.data_pvsclusters, (unsigned char *)(in + 1), totalchains);
5529 }
5530
5531 static void Mod_Q3BSP_LightPoint(dp_model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
5532 {
5533         int i, j, k, index[3];
5534         float transformed[3], blend1, blend2, blend, stylescale;
5535         q3dlightgrid_t *a, *s;
5536
5537         // scale lighting by lightstyle[0] so that darkmode in dpmod works properly
5538         stylescale = r_refdef.scene.rtlightstylevalue[0];
5539
5540         if (!model->brushq3.num_lightgrid)
5541         {
5542                 ambientcolor[0] = stylescale;
5543                 ambientcolor[1] = stylescale;
5544                 ambientcolor[2] = stylescale;
5545                 return;
5546         }
5547
5548         Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
5549         //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
5550         //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
5551         transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0] - 1);
5552         transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1] - 1);
5553         transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2] - 1);
5554         index[0] = (int)floor(transformed[0]);
5555         index[1] = (int)floor(transformed[1]);
5556         index[2] = (int)floor(transformed[2]);
5557         //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
5558
5559         // now lerp the values
5560         VectorClear(diffusenormal);
5561         a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
5562         for (k = 0;k < 2;k++)
5563         {
5564                 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
5565                 if (blend1 < 0.001f || index[2] + k >= model->brushq3.num_lightgrid_isize[2])
5566                         continue;
5567                 for (j = 0;j < 2;j++)
5568                 {
5569                         blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
5570                         if (blend2 < 0.001f || index[1] + j >= model->brushq3.num_lightgrid_isize[1])
5571                                 continue;
5572                         for (i = 0;i < 2;i++)
5573                         {
5574                                 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0]))) * stylescale;
5575                                 if (blend < 0.001f || index[0] + i >= model->brushq3.num_lightgrid_isize[0])
5576                                         continue;
5577                                 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
5578                                 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
5579                                 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
5580                                 // this uses the mod_md3_sin table because the values are
5581                                 // already in the 0-255 range, the 64+ bias fetches a cosine
5582                                 // instead of a sine value
5583                                 diffusenormal[0] += blend * (mod_md3_sin[64 + s->diffuseyaw] * mod_md3_sin[s->diffusepitch]);
5584                                 diffusenormal[1] += blend * (mod_md3_sin[     s->diffuseyaw] * mod_md3_sin[s->diffusepitch]);
5585                                 diffusenormal[2] += blend * (mod_md3_sin[64 + s->diffusepitch]);
5586                                 //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)));
5587                         }
5588                 }
5589         }
5590
5591         // normalize the light direction before turning
5592         VectorNormalize(diffusenormal);
5593         //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]);
5594 }
5595
5596 static int Mod_Q3BSP_TraceLineOfSight_RecursiveNodeCheck(mnode_t *node, double p1[3], double p2[3])
5597 {
5598         double t1, t2;
5599         double midf, mid[3];
5600         int ret, side;
5601
5602         // check for empty
5603         while (node->plane)
5604         {
5605                 // find the point distances
5606                 mplane_t *plane = node->plane;
5607                 if (plane->type < 3)
5608                 {
5609                         t1 = p1[plane->type] - plane->dist;
5610                         t2 = p2[plane->type] - plane->dist;
5611                 }
5612                 else
5613                 {
5614                         t1 = DotProduct (plane->normal, p1) - plane->dist;
5615                         t2 = DotProduct (plane->normal, p2) - plane->dist;
5616                 }
5617
5618                 if (t1 < 0)
5619                 {
5620                         if (t2 < 0)
5621                         {
5622                                 node = node->children[1];
5623                                 continue;
5624                         }
5625                         side = 1;
5626                 }
5627                 else
5628                 {
5629                         if (t2 >= 0)
5630                         {
5631                                 node = node->children[0];
5632                                 continue;
5633                         }
5634                         side = 0;
5635                 }
5636
5637                 midf = t1 / (t1 - t2);
5638                 VectorLerp(p1, midf, p2, mid);
5639
5640                 // recurse both sides, front side first
5641                 // return 2 if empty is followed by solid (hit something)
5642                 // do not return 2 if both are solid or both empty,
5643                 // or if start is solid and end is empty
5644                 // as these degenerate cases usually indicate the eye is in solid and
5645                 // should see the target point anyway
5646                 ret = Mod_Q3BSP_TraceLineOfSight_RecursiveNodeCheck(node->children[side    ], p1, mid);
5647                 if (ret != 0)
5648                         return ret;
5649                 ret = Mod_Q3BSP_TraceLineOfSight_RecursiveNodeCheck(node->children[side ^ 1], mid, p2);
5650                 if (ret != 1)
5651                         return ret;
5652                 return 2;
5653         }
5654         return ((mleaf_t *)node)->clusterindex < 0;
5655 }
5656
5657 static qboolean Mod_Q3BSP_TraceLineOfSight(struct model_s *model, const vec3_t start, const vec3_t end)
5658 {
5659         if (model->brush.submodel || mod_q3bsp_tracelineofsight_brushes.integer)
5660         {
5661                 trace_t trace;
5662                 model->TraceLine(model, NULL, NULL, &trace, start, end, SUPERCONTENTS_VISBLOCKERMASK);
5663                 return trace.fraction == 1;
5664         }
5665         else
5666         {
5667                 double tracestart[3], traceend[3];
5668                 VectorCopy(start, tracestart);
5669                 VectorCopy(end, traceend);
5670                 return !Mod_Q3BSP_TraceLineOfSight_RecursiveNodeCheck(model->brush.data_nodes, tracestart, traceend);
5671         }
5672 }
5673
5674 static void Mod_BIH_TracePoint_RecursiveBIHNode(trace_t *trace, dp_model_t *model, int nodenum, const vec3_t point)
5675 {
5676         const bih_leaf_t *leaf;
5677         const bih_node_t *node;
5678         const colbrushf_t *brush;
5679         int axis;
5680         while (nodenum >= 0)
5681         {
5682                 node = model->collision_bih.nodes + nodenum;
5683                 axis = node->type - BIH_SPLITX;
5684                 if (point[axis] <= node->backmax)
5685                 {
5686                         if (point[axis] >= node->frontmin)
5687                                 Mod_BIH_TracePoint_RecursiveBIHNode(trace, model, node->front, point);
5688                         nodenum = node->back;
5689                 }
5690                 else if (point[axis] >= node->frontmin)
5691                         nodenum = node->front;
5692                 else // no overlap with either child?  just return
5693                         return;
5694         }
5695         if (!model->collision_bih.leafs)
5696                 return;
5697         leaf = model->collision_bih.leafs + (-1-nodenum);
5698         switch(leaf->type)
5699         {
5700         case BIH_LEAF:
5701                 // brush
5702                 brush = model->brush.data_brushes[leaf->itemindex].colbrushf;
5703                 Collision_TracePointBrushFloat(trace, point, brush);
5704                 break;
5705         case BIH_LEAF + 1:
5706                 // triangle - skipped because they have no volume
5707                 break;
5708         default:
5709                 break;
5710         }
5711 }
5712
5713 static void Mod_BIH_TraceLine_RecursiveBIHNode(trace_t *trace, dp_model_t *model, int nodenum, const vec3_t start, const vec3_t end, const vec3_t linestart, const vec3_t lineend)
5714 {
5715         const bih_leaf_t *leaf;
5716         const bih_node_t *node;
5717         const colbrushf_t *brush;
5718         const int *e;
5719         const texture_t *texture;
5720         int axis;
5721 #if 0
5722         int sideflags;
5723         vec_t frontdist1;
5724         vec_t frontdist2;
5725         vec_t frontfrac;
5726         vec_t backdist1;
5727         vec_t backdist2;
5728         vec_t backfrac;
5729         vec3_t clipped[2];
5730 #endif
5731         vec3_t segmentmins;
5732         vec3_t segmentmaxs;
5733         segmentmins[0] = min(start[0], end[0]);
5734         segmentmins[1] = min(start[1], end[1]);
5735         segmentmins[2] = min(start[2], end[2]);
5736         segmentmaxs[0] = max(start[0], end[0]);
5737         segmentmaxs[1] = max(start[1], end[1]);
5738         segmentmaxs[2] = max(start[2], end[2]);
5739         while (nodenum >= 0)
5740         {
5741                 node = model->collision_bih.nodes + nodenum;
5742 #if 0
5743                 if (!BoxesOverlap(segmentmins, segmentmaxs, node->mins, node->maxs))
5744                         return;
5745 #endif
5746                 axis = node->type - BIH_SPLITX;
5747 #if 1
5748                 if (segmentmins[axis] <= node->backmax)
5749                 {
5750                         if (segmentmaxs[axis] >= node->frontmin)
5751                                 Mod_BIH_TraceLine_RecursiveBIHNode(trace, model, node->front, start, end, linestart, lineend);
5752                         nodenum = node->back;
5753                 }
5754                 else if (segmentmaxs[axis] >= node->frontmin)
5755                         nodenum = node->front;
5756                 else
5757                         return; // trace falls between children
5758 #else
5759                 frontdist1 = start[axis] - node->backmax;
5760                 frontdist2 = end[axis] - node->backmax;
5761                 backdist1 = start[axis] - node->frontmin;
5762                 backdist2 = end[axis] - node->frontmin;
5763                 sideflags = 0;
5764                 if (frontdist1 < 0)
5765                         sideflags |= 1;
5766                 if (frontdist2 < 0)
5767                         sideflags |= 2;
5768                 if (backdist1 < 0)
5769                         sideflags |= 4;
5770                 if (backdist2 < 0)
5771                         sideflags |= 8;
5772                 switch(sideflags)
5773                 {
5774                 case 0:
5775                         // start end START END
5776                         nodenum = node->front;
5777                         continue;
5778                 case 1:
5779                         // START end START END
5780                         frontfrac = frontdist1 / (frontdist1 - frontdist2);
5781                         VectorLerp(start, frontfrac, end, clipped[0]);
5782                         start = clipped[0];
5783                         segmentmins[0] = min(start[0], end[0]);
5784                         segmentmins[1] = min(start[1], end[1]);
5785                         segmentmins[2] = min(start[2], end[2]);
5786                         segmentmaxs[0] = max(start[0], end[0]);
5787                         segmentmaxs[1] = max(start[1], end[1]);
5788                         segmentmaxs[2] = max(start[2], end[2]);
5789                         nodenum = node->front;
5790                         break;
5791                 case 2:
5792                         // start END START END
5793                         frontfrac = frontdist1 / (frontdist1 - frontdist2);
5794                         VectorLerp(start, frontfrac, end, clipped[0]);
5795                         end = clipped[0];
5796                         segmentmins[0] = min(start[0], end[0]);
5797                         segmentmins[1] = min(start[1], end[1]);
5798                         segmentmins[2] = min(start[2], end[2]);
5799                         segmentmaxs[0] = max(start[0], end[0]);
5800                         segmentmaxs[1] = max(start[1], end[1]);
5801                         segmentmaxs[2] = max(start[2], end[2]);
5802                         nodenum = node->front;
5803                         break;
5804                 case 3:
5805                         // START END START END
5806                         return; // line falls in gap between children
5807                 case 4:
5808                         // start end start END
5809                         Mod_BIH_TraceLine_RecursiveBIHNode(trace, model, node->front, start, end, linestart, lineend);
5810                         backfrac = backdist1 / (backdist1 - backdist2);
5811                         VectorLerp(start, backfrac, end, clipped[0]);
5812                         end = clipped[0];
5813                         segmentmins[0] = min(start[0], end[0]);
5814                         segmentmins[1] = min(start[1], end[1]);
5815                         segmentmins[2] = min(start[2], end[2]);
5816                         segmentmaxs[0] = max(start[0], end[0]);
5817                         segmentmaxs[1] = max(start[1], end[1]);
5818                         segmentmaxs[2] = max(start[2], end[2]);
5819                         nodenum = node->back;
5820                         break;
5821                 case 5:
5822                         // START end start END
5823                         frontfrac = frontdist1 / (frontdist1 - frontdist2);
5824                         VectorLerp(start, frontfrac, end, clipped[1]);
5825                         Mod_BIH_TraceLine_RecursiveBIHNode(trace, model, node->front, clipped[1], end, linestart, lineend);
5826                         backfrac = backdist1 / (backdist1 - backdist2);
5827                         VectorLerp(start, backfrac, end, clipped[0]);
5828                         end = clipped[0];
5829                         segmentmins[0] = min(start[0], end[0]);
5830                         segmentmins[1] = min(start[1], end[1]);
5831                         segmentmins[2] = min(start[2], end[2]);
5832                         segmentmaxs[0] = max(start[0], end[0]);
5833                         segmentmaxs[1] = max(start[1], end[1]);
5834                         segmentmaxs[2] = max(start[2], end[2]);
5835                         nodenum = node->back;
5836                         break;
5837                 case 6:
5838                         // start END start END
5839                         frontfrac = frontdist1 / (frontdist1 - frontdist2);
5840                         VectorLerp(start, frontfrac, end, clipped[1]);
5841                         Mod_BIH_TraceLine_RecursiveBIHNode(trace, model, node->front, start, clipped[1], linestart, lineend);
5842                         backfrac = backdist1 / (backdist1 - backdist2);
5843                         VectorLerp(start, backfrac, end, clipped[0]);
5844                         end = clipped[0];
5845                         segmentmins[0] = min(start[0], end[0]);
5846                         segmentmins[1] = min(start[1], end[1]);
5847                         segmentmins[2] = min(start[2], end[2]);
5848                         segmentmaxs[0] = max(start[0], end[0]);
5849                         segmentmaxs[1] = max(start[1], end[1]);
5850                         segmentmaxs[2] = max(start[2], end[2]);
5851                         nodenum = node->back;
5852                         break;
5853                 case 7:
5854                         // START END start END
5855                         backfrac = backdist1 / (backdist1 - backdist2);
5856                         VectorLerp(start, backfrac, end, clipped[0]);
5857                         end = clipped[0];
5858                         segmentmins[0] = min(start[0], end[0]);
5859                         segmentmins[1] = min(start[1], end[1]);
5860                         segmentmins[2] = min(start[2], end[2]);
5861                         segmentmaxs[0] = max(start[0], end[0]);
5862                         segmentmaxs[1] = max(start[1], end[1]);
5863                         segmentmaxs[2] = max(start[2], end[2]);
5864                         nodenum = node->back;
5865                         break;
5866                 case 8:
5867                         // start end START end
5868                         Mod_BIH_TraceLine_RecursiveBIHNode(trace, model, node->front, start, end, linestart, lineend);
5869                         backfrac = backdist1 / (backdist1 - backdist2);
5870                         VectorLerp(start, backfrac, end, clipped[0]);
5871                         start = clipped[0];
5872                         segmentmins[0] = min(start[0], end[0]);
5873                         segmentmins[1] = min(start[1], end[1]);
5874                         segmentmins[2] = min(start[2], end[2]);
5875                         segmentmaxs[0] = max(start[0], end[0]);
5876                         segmentmaxs[1] = max(start[1], end[1]);
5877                         segmentmaxs[2] = max(start[2], end[2]);
5878                         nodenum = node->back;
5879                         break;
5880                 case 9:
5881                         // START end START end
5882                         frontfrac = frontdist1 / (frontdist1 - frontdist2);
5883                         VectorLerp(start, frontfrac, end, clipped[1]);
5884                         Mod_BIH_TraceLine_RecursiveBIHNode(trace, model, node->front, clipped[1], end, linestart, lineend);
5885                         backfrac = backdist1 / (backdist1 - backdist2);
5886                         VectorLerp(start, backfrac, end, clipped[0]);
5887                         start = clipped[0];
5888                         segmentmins[0] = min(start[0], end[0]);
5889                         segmentmins[1] = min(start[1], end[1]);
5890                         segmentmins[2] = min(start[2], end[2]);
5891                         segmentmaxs[0] = max(start[0], end[0]);
5892                         segmentmaxs[1] = max(start[1], end[1]);
5893                         segmentmaxs[2] = max(start[2], end[2]);
5894                         nodenum = node->back;
5895                         break;
5896                 case 10:
5897                         // start END START end
5898                         frontfrac = frontdist1 / (frontdist1 - frontdist2);
5899                         VectorLerp(start, frontfrac, end, clipped[1]);
5900                         Mod_BIH_TraceLine_RecursiveBIHNode(trace, model, node->front, start, clipped[1], linestart, lineend);
5901                         backfrac = backdist1 / (backdist1 - backdist2);
5902                         VectorLerp(start, backfrac, end, clipped[0]);
5903                         start = clipped[0];
5904                         segmentmins[0] = min(start[0], end[0]);
5905                         segmentmins[1] = min(start[1], end[1]);
5906                         segmentmins[2] = min(start[2], end[2]);
5907                         segmentmaxs[0] = max(start[0], end[0]);
5908                         segmentmaxs[1] = max(start[1], end[1]);
5909                         segmentmaxs[2] = max(start[2], end[2]);
5910                         nodenum = node->back;
5911                         break;
5912                 case 11:
5913                         // START END START end
5914                         backfrac = backdist1 / (backdist1 - backdist2);
5915                         VectorLerp(start, backfrac, end, clipped[0]);
5916                         start = clipped[0];
5917                         segmentmins[0] = min(start[0], end[0]);
5918                         segmentmins[1] = min(start[1], end[1]);
5919                         segmentmins[2] = min(start[2], end[2]);
5920                         segmentmaxs[0] = max(start[0], end[0]);
5921                         segmentmaxs[1] = max(start[1], end[1]);
5922                         segmentmaxs[2] = max(start[2], end[2]);
5923                         nodenum = node->back;
5924                         break;
5925                 case 12:
5926                         // start end start end
5927                         Mod_BIH_TraceLine_RecursiveBIHNode(trace, model, node->front, start, end, linestart, lineend);
5928                         nodenum = node->back;
5929                         break;
5930                 case 13:
5931                         // START end start end
5932                         frontfrac = frontdist1 / (frontdist1 - frontdist2);
5933                         VectorLerp(start, frontfrac, end, clipped[1]);
5934                         Mod_BIH_TraceLine_RecursiveBIHNode(trace, model, node->front, clipped[1], end, linestart, lineend);
5935                         nodenum = node->back;
5936                         break;
5937                 case 14:
5938                         // start END start end
5939                         frontfrac = frontdist1 / (frontdist1 - frontdist2);
5940                         VectorLerp(start, frontfrac, end, clipped[1]);
5941                         Mod_BIH_TraceLine_RecursiveBIHNode(trace, model, node->front, start, clipped[1], linestart, lineend);
5942                         nodenum = node->back;
5943                         break;
5944                 case 15:
5945                         // START END start end
5946                         nodenum = node->back;
5947                         continue;
5948                 }
5949 #endif
5950         }
5951         if (!model->collision_bih.leafs)
5952                 return;
5953         leaf = model->collision_bih.leafs + (-1-nodenum);
5954 #if 1
5955         if (!BoxesOverlap(segmentmins, segmentmaxs, leaf->mins, leaf->maxs))
5956                 return;
5957 #endif
5958         switch(leaf->type)
5959         {
5960         case BIH_LEAF:
5961                 // brush
5962                 brush = model->brush.data_brushes[leaf->itemindex].colbrushf;
5963                 Collision_TraceLineBrushFloat(trace, linestart, lineend, brush, brush);
5964                 break;
5965         case BIH_LEAF + 1:
5966                 // triangle
5967                 e = model->brush.data_collisionelement3i + 3*leaf->itemindex;
5968                 texture = model->data_textures + leaf->textureindex;
5969                 Collision_TraceLineTriangleFloat(trace, linestart, lineend, model->brush.data_collisionvertex3f + e[0] * 3, model->brush.data_collisionvertex3f + e[1] * 3, model->brush.data_collisionvertex3f + e[2] * 3, texture->supercontents, texture->surfaceflags, texture);
5970                 break;
5971         default:
5972                 break;
5973         }
5974 }
5975
5976 static void Mod_BIH_TraceBrush_RecursiveBIHNode(trace_t *trace, dp_model_t *model, int nodenum, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, const vec3_t segmentmins, const vec3_t segmentmaxs)
5977 {
5978         const bih_leaf_t *leaf;
5979         const bih_node_t *node;
5980         const colbrushf_t *brush;
5981         const int *e;
5982         const texture_t *texture;
5983         int axis;
5984         while (nodenum >= 0)
5985         {
5986                 node = model->collision_bih.nodes + nodenum;
5987                 axis = node->type - BIH_SPLITX;
5988 #if 0
5989 #if 0
5990                 if (!BoxesOverlap(segmentmins, segmentmaxs, node->mins, node->maxs))
5991                         return;
5992 #endif
5993                 Mod_BIH_TraceBrush_RecursiveBIHNode(trace, model, node->front, thisbrush_start, thisbrush_end, segmentmins, segmentmaxs);
5994                 nodenum = node->back;
5995                 continue;
5996 #endif
5997                 if (segmentmins[axis] <= node->backmax)
5998                 {
5999                         if (segmentmaxs[axis] >= node->frontmin)
6000                                 Mod_BIH_TraceBrush_RecursiveBIHNode(trace, model, node->front, thisbrush_start, thisbrush_end, segmentmins, segmentmaxs);
6001                         nodenum = node->back;
6002                 }
6003                 else if (segmentmaxs[axis] >= node->frontmin)
6004                         nodenum = node->front;
6005                 else
6006                         return; // trace falls between children
6007         }
6008         if (!model->collision_bih.leafs)
6009                 return;
6010         leaf = model->collision_bih.leafs + (-1-nodenum);
6011 #if 1
6012         if (!BoxesOverlap(segmentmins, segmentmaxs, leaf->mins, leaf->maxs))
6013                 return;
6014 #endif
6015         switch(leaf->type)
6016         {
6017         case BIH_LEAF:
6018                 // brush
6019                 brush = model->brush.data_brushes[leaf->itemindex].colbrushf;
6020                 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, brush, brush);
6021                 break;
6022         case BIH_LEAF + 1:
6023                 // triangle
6024                 e = model->brush.data_collisionelement3i + 3*leaf->itemindex;
6025                 texture = model->data_textures + leaf->textureindex;
6026                 Collision_TraceBrushTriangleFloat(trace, thisbrush_start, thisbrush_end, model->brush.data_collisionvertex3f + e[0] * 3, model->brush.data_collisionvertex3f + e[1] * 3, model->brush.data_collisionvertex3f + e[2] * 3, texture->supercontents, texture->surfaceflags, texture);
6027                 break;
6028         default:
6029                 break;
6030         }
6031 }
6032
6033 static void Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace_t *trace, dp_model_t *model, mnode_t *node, const vec3_t point, int markframe)
6034 {
6035         int i;
6036         mleaf_t *leaf;
6037         colbrushf_t *brush;
6038         // find which leaf the point is in
6039         while (node->plane)
6040                 node = node->children[(node->plane->type < 3 ? point[node->plane->type] : DotProduct(point, node->plane->normal)) < node->plane->dist];
6041         // point trace the brushes
6042         leaf = (mleaf_t *)node;
6043         for (i = 0;i < leaf->numleafbrushes;i++)
6044         {
6045                 brush = model->brush.data_brushes[leaf->firstleafbrush[i]].colbrushf;
6046                 if (brush && brush->markframe != markframe && BoxesOverlap(point, point, brush->mins, brush->maxs))
6047                 {
6048                         brush->markframe = markframe;
6049                         Collision_TracePointBrushFloat(trace, point, brush);
6050                 }
6051         }
6052         // can't do point traces on curves (they have no thickness)
6053 }
6054
6055 static void Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace_t *trace, dp_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)
6056 {
6057         int i, startside, endside;
6058         float dist1, dist2, midfrac, mid[3], nodesegmentmins[3], nodesegmentmaxs[3];
6059         mleaf_t *leaf;
6060         msurface_t *surface;
6061         mplane_t *plane;
6062         colbrushf_t *brush;
6063         // walk the tree until we hit a leaf, recursing for any split cases
6064         while (node->plane)
6065         {
6066                 // abort if this part of the bsp tree can not be hit by this trace
6067 //              if (!(node->combinedsupercontents & trace->hitsupercontentsmask))
6068 //                      return;
6069                 plane = node->plane;
6070                 // axial planes are much more common than non-axial, so an optimized
6071                 // axial case pays off here
6072                 if (plane->type < 3)
6073                 {
6074                         dist1 = start[plane->type] - plane->dist;
6075                         dist2 = end[plane->type] - plane->dist;
6076                 }
6077                 else
6078                 {
6079                         dist1 = DotProduct(start, plane->normal) - plane->dist;
6080                         dist2 = DotProduct(end, plane->normal) - plane->dist;
6081                 }
6082                 startside = dist1 < 0;
6083                 endside = dist2 < 0;
6084                 if (startside == endside)
6085                 {
6086                         // most of the time the line fragment is on one side of the plane
6087                         node = node->children[startside];
6088                 }
6089                 else
6090                 {
6091                         // line crosses node plane, split the line
6092                         dist1 = PlaneDiff(linestart, plane);
6093                         dist2 = PlaneDiff(lineend, plane);
6094                         midfrac = dist1 / (dist1 - dist2);
6095                         VectorLerp(linestart, midfrac, lineend, mid);
6096                         // take the near side first
6097                         Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
6098                         // if we found an impact on the front side, don't waste time
6099                         // exploring the far side
6100                         if (midfrac <= trace->realfraction)
6101                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
6102                         return;
6103                 }
6104         }
6105         // abort if this part of the bsp tree can not be hit by this trace
6106 //      if (!(node->combinedsupercontents & trace->hitsupercontentsmask))
6107 //              return;
6108         // hit a leaf
6109         nodesegmentmins[0] = min(start[0], end[0]) - 1;
6110         nodesegmentmins[1] = min(start[1], end[1]) - 1;
6111         nodesegmentmins[2] = min(start[2], end[2]) - 1;
6112         nodesegmentmaxs[0] = max(start[0], end[0]) + 1;
6113         nodesegmentmaxs[1] = max(start[1], end[1]) + 1;
6114         nodesegmentmaxs[2] = max(start[2], end[2]) + 1;
6115         // line trace the brushes
6116         leaf = (mleaf_t *)node;
6117         for (i = 0;i < leaf->numleafbrushes;i++)
6118         {
6119                 brush = model->brush.data_brushes[leaf->firstleafbrush[i]].colbrushf;
6120                 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
6121                 {
6122                         brush->markframe = markframe;
6123                         Collision_TraceLineBrushFloat(trace, linestart, lineend, brush, brush);
6124                 }
6125         }
6126         // can't do point traces on curves (they have no thickness)
6127         if (leaf->containscollisionsurfaces && mod_q3bsp_curves_collisions.integer && !VectorCompare(start, end))
6128         {
6129                 // line trace the curves
6130                 for (i = 0;i < leaf->numleafsurfaces;i++)
6131                 {
6132                         surface = model->data_surfaces + leaf->firstleafsurface[i];
6133                         if (surface->num_collisiontriangles && surface->deprecatedq3collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, surface->mins, surface->maxs))
6134                         {
6135                                 surface->deprecatedq3collisionmarkframe = markframe;
6136                                 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, surface->num_collisiontriangles, surface->deprecatedq3data_collisionelement3i, surface->deprecatedq3data_collisionvertex3f, surface->deprecatedq3num_collisionbboxstride, surface->deprecatedq3data_collisionbbox6f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
6137                         }
6138                 }
6139         }
6140 }
6141
6142 static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, dp_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)
6143 {
6144         int i;
6145         int sides;
6146         mleaf_t *leaf;
6147         colbrushf_t *brush;
6148         msurface_t *surface;
6149         mplane_t *plane;
6150         float nodesegmentmins[3], nodesegmentmaxs[3];
6151         // walk the tree until we hit a leaf, recursing for any split cases
6152         while (node->plane)
6153         {
6154                 // abort if this part of the bsp tree can not be hit by this trace
6155 //              if (!(node->combinedsupercontents & trace->hitsupercontentsmask))
6156 //                      return;
6157                 plane = node->plane;
6158                 // axial planes are much more common than non-axial, so an optimized
6159                 // axial case pays off here
6160                 if (plane->type < 3)
6161                 {
6162                         // this is an axial plane, compare bounding box directly to it and
6163                         // recurse sides accordingly
6164                         // recurse down node sides
6165                         // use an inlined axial BoxOnPlaneSide to slightly reduce overhead
6166                         //sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, plane);
6167                         //sides = ((segmentmaxs[plane->type] >= plane->dist) | ((segmentmins[plane->type] < plane->dist) << 1));
6168                         sides = ((segmentmaxs[plane->type] >= plane->dist) + ((segmentmins[plane->type] < plane->dist) * 2));
6169                 }
6170                 else
6171                 {
6172                         // this is a non-axial plane, so check if the start and end boxes
6173                         // are both on one side of the plane to handle 'diagonal' cases
6174                         sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, plane);
6175                 }
6176                 if (sides == 3)
6177                 {
6178                         // segment crosses plane
6179                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
6180                         sides = 2;
6181                 }
6182                 // if sides == 0 then the trace itself is bogus (Not A Number values),
6183                 // in this case we simply pretend the trace hit nothing
6184                 if (sides == 0)
6185                         return; // ERROR: NAN bounding box!
6186                 // take whichever side the segment box is on
6187                 node = node->children[sides - 1];
6188         }
6189         // abort if this part of the bsp tree can not be hit by this trace
6190 //      if (!(node->combinedsupercontents & trace->hitsupercontentsmask))
6191 //              return;
6192         nodesegmentmins[0] = max(segmentmins[0], node->mins[0] - 1);
6193         nodesegmentmins[1] = max(segmentmins[1], node->mins[1] - 1);
6194         nodesegmentmins[2] = max(segmentmins[2], node->mins[2] - 1);
6195         nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0] + 1);
6196         nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1] + 1);
6197         nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2] + 1);
6198         // hit a leaf
6199         leaf = (mleaf_t *)node;
6200         for (i = 0;i < leaf->numleafbrushes;i++)
6201         {
6202                 brush = model->brush.data_brushes[leaf->firstleafbrush[i]].colbrushf;
6203                 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
6204                 {
6205                         brush->markframe = markframe;
6206                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, brush, brush);
6207                 }
6208         }
6209         if (leaf->containscollisionsurfaces && mod_q3bsp_curves_collisions.integer)
6210         {
6211                 for (i = 0;i < leaf->numleafsurfaces;i++)
6212                 {
6213                         surface = model->data_surfaces + leaf->firstleafsurface[i];
6214                         if (surface->num_collisiontriangles && surface->deprecatedq3collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, surface->mins, surface->maxs))
6215                         {
6216                                 surface->deprecatedq3collisionmarkframe = markframe;
6217                                 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, surface->num_collisiontriangles, surface->deprecatedq3data_collisionelement3i, surface->deprecatedq3data_collisionvertex3f, surface->deprecatedq3num_collisionbboxstride, surface->deprecatedq3data_collisionbbox6f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
6218                         }
6219                 }
6220         }
6221 }
6222
6223 static int markframe = 0;
6224
6225 static void Mod_Q3BSP_TracePoint(dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, const vec3_t start, int hitsupercontentsmask)
6226 {
6227         int i;
6228         q3mbrush_t *brush;
6229         memset(trace, 0, sizeof(*trace));
6230         trace->fraction = 1;
6231         trace->realfraction = 1;
6232         trace->hitsupercontentsmask = hitsupercontentsmask;
6233         if (mod_collision_bih.integer)
6234                 Mod_BIH_TracePoint_RecursiveBIHNode(trace, model, model->collision_bih.rootnode, start);
6235         else if (model->brush.submodel)
6236         {
6237                 for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
6238                         if (brush->colbrushf)
6239                                 Collision_TracePointBrushFloat(trace, start, brush->colbrushf);
6240         }
6241         else
6242                 Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace, model, model->brush.data_nodes, start, ++markframe);
6243 }
6244
6245 static void Mod_Q3BSP_TraceLine(dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, const vec3_t start, const vec3_t end, int hitsupercontentsmask)
6246 {
6247         int i;
6248         float segmentmins[3], segmentmaxs[3];
6249         msurface_t *surface;
6250         q3mbrush_t *brush;
6251
6252         if (VectorCompare(start, end))
6253         {
6254                 Mod_Q3BSP_TracePoint(model, frameblend, skeleton, trace, start, hitsupercontentsmask);
6255                 return;
6256         }
6257
6258         memset(trace, 0, sizeof(*trace));
6259         trace->fraction = 1;
6260         trace->realfraction = 1;
6261         trace->hitsupercontentsmask = hitsupercontentsmask;
6262         segmentmins[0] = min(start[0], end[0]) - 1;
6263         segmentmins[1] = min(start[1], end[1]) - 1;
6264         segmentmins[2] = min(start[2], end[2]) - 1;
6265         segmentmaxs[0] = max(start[0], end[0]) + 1;
6266         segmentmaxs[1] = max(start[1], end[1]) + 1;
6267         segmentmaxs[2] = max(start[2], end[2]) + 1;
6268         if (mod_collision_bih.integer)
6269                 Mod_BIH_TraceLine_RecursiveBIHNode(trace, model, model->collision_bih.rootnode, start, end, start, end);
6270         else if (model->brush.submodel)
6271         {
6272                 for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
6273                         if (brush->colbrushf)
6274                                 Collision_TraceLineBrushFloat(trace, start, end, brush->colbrushf, brush->colbrushf);
6275                 if (mod_q3bsp_curves_collisions.integer)
6276                         for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
6277                                 if (surface->num_collisiontriangles)
6278                                         Collision_TraceLineTriangleMeshFloat(trace, start, end, surface->num_collisiontriangles, surface->deprecatedq3data_collisionelement3i, surface->deprecatedq3data_collisionvertex3f, surface->deprecatedq3num_collisionbboxstride, surface->deprecatedq3data_collisionbbox6f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
6279         }
6280         else
6281                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, model->brush.data_nodes, start, end, 0, 1, start, end, ++markframe, segmentmins, segmentmaxs);
6282 }
6283
6284 static void Mod_Q3BSP_TraceBox(dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask)
6285 {
6286         int i;
6287         float segmentmins[3], segmentmaxs[3];
6288         msurface_t *surface;
6289         q3mbrush_t *brush;
6290         colboxbrushf_t thisbrush_start, thisbrush_end;
6291         vec3_t boxstartmins, boxstartmaxs, boxendmins, boxendmaxs;
6292
6293         if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxmins, boxmaxs))
6294         {
6295                 vec3_t shiftstart, shiftend;
6296                 VectorAdd(start, boxmins, shiftstart);
6297                 VectorAdd(end, boxmins, shiftend);
6298                 if (VectorCompare(start, end))
6299                         Mod_Q3BSP_TracePoint(model, frameblend, skeleton, trace, shiftstart, hitsupercontentsmask);
6300                 else
6301                 {
6302                         Mod_Q3BSP_TraceLine(model, frameblend, skeleton, trace, shiftstart, shiftend, hitsupercontentsmask);
6303                         VectorSubtract(trace->endpos, boxmins, trace->endpos);
6304                 }
6305                 return;
6306         }
6307
6308         // box trace, performed as brush trace
6309         memset(trace, 0, sizeof(*trace));
6310         trace->fraction = 1;
6311         trace->realfraction = 1;
6312         trace->hitsupercontentsmask = hitsupercontentsmask;
6313         segmentmins[0] = min(start[0], end[0]) + boxmins[0] - 1;
6314         segmentmins[1] = min(start[1], end[1]) + boxmins[1] - 1;
6315         segmentmins[2] = min(start[2], end[2]) + boxmins[2] - 1;
6316         segmentmaxs[0] = max(start[0], end[0]) + boxmaxs[0] + 1;
6317         segmentmaxs[1] = max(start[1], end[1]) + boxmaxs[1] + 1;
6318         segmentmaxs[2] = max(start[2], end[2]) + boxmaxs[2] + 1;
6319         VectorAdd(start, boxmins, boxstartmins);
6320         VectorAdd(start, boxmaxs, boxstartmaxs);
6321         VectorAdd(end, boxmins, boxendmins);
6322         VectorAdd(end, boxmaxs, boxendmaxs);
6323         Collision_BrushForBox(&thisbrush_start, boxstartmins, boxstartmaxs, 0, 0, NULL);
6324         Collision_BrushForBox(&thisbrush_end, boxendmins, boxendmaxs, 0, 0, NULL);
6325         if (mod_collision_bih.integer)
6326                 Mod_BIH_TraceBrush_RecursiveBIHNode(trace, model, model->collision_bih.rootnode, &thisbrush_start.brush, &thisbrush_end.brush, segmentmins, segmentmaxs);
6327         else if (model->brush.submodel)
6328         {
6329                 for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
6330                         if (brush->colbrushf)
6331                                 Collision_TraceBrushBrushFloat(trace, &thisbrush_start.brush, &thisbrush_end.brush, brush->colbrushf, brush->colbrushf);
6332                 if (mod_q3bsp_curves_collisions.integer)
6333                         for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
6334                                 if (surface->num_collisiontriangles)
6335                                         Collision_TraceBrushTriangleMeshFloat(trace, &thisbrush_start.brush, &thisbrush_end.brush, surface->num_collisiontriangles, surface->deprecatedq3data_collisionelement3i, surface->deprecatedq3data_collisionvertex3f, surface->deprecatedq3num_collisionbboxstride, surface->deprecatedq3data_collisionbbox6f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
6336         }
6337         else
6338                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, model->brush.data_nodes, &thisbrush_start.brush, &thisbrush_end.brush, ++markframe, segmentmins, segmentmaxs);
6339 }
6340
6341 static int Mod_Q3BSP_PointSuperContents(struct model_s *model, int frame, const vec3_t point)
6342 {
6343         int i;
6344         int supercontents = 0;
6345         q3mbrush_t *brush;
6346         if (mod_collision_bih.integer)
6347         {
6348                 trace_t trace;
6349                 Mod_Q3BSP_TracePoint(model, NULL, NULL, &trace, point, 0);
6350                 supercontents = trace.startsupercontents;
6351         }
6352         // test if the point is inside each brush
6353         else if (model->brush.submodel)
6354         {
6355                 // submodels are effectively one leaf
6356                 for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
6357                         if (brush->colbrushf && Collision_PointInsideBrushFloat(point, brush->colbrushf))
6358                                 supercontents |= brush->colbrushf->supercontents;
6359         }
6360         else
6361         {
6362                 mnode_t *node = model->brush.data_nodes;
6363                 mleaf_t *leaf;
6364                 // find which leaf the point is in
6365                 while (node->plane)
6366                         node = node->children[(node->plane->type < 3 ? point[node->plane->type] : DotProduct(point, node->plane->normal)) < node->plane->dist];
6367                 leaf = (mleaf_t *)node;
6368                 // now check the brushes in the leaf
6369                 for (i = 0;i < leaf->numleafbrushes;i++)
6370                 {
6371                         brush = model->brush.data_brushes + leaf->firstleafbrush[i];
6372                         if (brush->colbrushf && Collision_PointInsideBrushFloat(point, brush->colbrushf))
6373                                 supercontents |= brush->colbrushf->supercontents;
6374                 }
6375         }
6376         return supercontents;
6377 }
6378
6379 void Mod_MakeCollisionData(dp_model_t *model)
6380 {
6381         int j;
6382         int bihnumleafs;
6383         int bihmaxnodes;
6384         int brushindex;
6385         int triangleindex;
6386         int bihleafindex;
6387         int nummodelbrushes = model->nummodelbrushes;
6388         int nummodelsurfaces = model->nummodelsurfaces;
6389         const int *e;
6390         const int *collisionelement3i;
6391         const float *collisionvertex3f;
6392         bih_leaf_t *bihleafs;
6393         bih_node_t *bihnodes;
6394         int *temp_leafsort;
6395         int *temp_leafsortscratch;
6396         const msurface_t *surface;
6397         const q3mbrush_t *brush;
6398
6399         // find out how many BIH leaf nodes we need
6400         bihnumleafs = model->nummodelbrushes;
6401         surface = model->data_surfaces + model->firstmodelsurface;
6402         for (j = 0, surface = model->data_surfaces + model->firstmodelsurface;j < nummodelsurfaces;j++, surface++)
6403                 bihnumleafs += surface->num_collisiontriangles;
6404         bihmaxnodes = bihnumleafs - 1;
6405
6406         // allocate the memory for the BIH leaf nodes
6407         bihleafs = Mem_Alloc(loadmodel->mempool, sizeof(bih_leaf_t) * bihnumleafs);
6408
6409         // add BIH leaf nodes for all the collision brushes
6410         bihleafindex = 0;
6411         for (brushindex = 0, brush = model->brush.data_brushes + brushindex+model->firstmodelbrush;brushindex < nummodelbrushes;brushindex++, brush++)
6412         {
6413                 bihleafs[bihleafindex].type = BIH_LEAF;
6414                 bihleafs[bihleafindex].textureindex = brush->texture - model->data_textures;
6415                 bihleafs[bihleafindex].itemindex = brushindex+model->firstmodelbrush;
6416                 VectorCopy(brush->colbrushf->mins, bihleafs[bihleafindex].mins);
6417                 VectorCopy(brush->colbrushf->maxs, bihleafs[bihleafindex].maxs);
6418                 bihleafindex++;
6419         }
6420
6421         // add BIH leaf nodes for all the collision surfaces
6422         collisionelement3i = model->brush.data_collisionelement3i;
6423         collisionvertex3f = model->brush.data_collisionvertex3f;
6424         for (j = 0, surface = model->data_surfaces + model->firstmodelsurface;j < nummodelsurfaces;j++, surface++)
6425         {
6426                 e = collisionelement3i + 3*surface->num_firstcollisiontriangle;
6427                 for (triangleindex = 0;triangleindex < surface->num_collisiontriangles;triangleindex++, e += 3)
6428                 {
6429                         bihleafs[bihleafindex].type = BIH_LEAF + 1;
6430                         bihleafs[bihleafindex].textureindex = surface->texture - model->data_textures;
6431                         bihleafs[bihleafindex].itemindex = triangleindex+surface->num_firstcollisiontriangle;
6432                         bihleafs[bihleafindex].mins[0] = min(collisionvertex3f[3*e[0]+0], min(collisionvertex3f[3*e[1]+0], collisionvertex3f[3*e[2]+0])) - 1;
6433                         bihleafs[bihleafindex].mins[1] = min(collisionvertex3f[3*e[0]+1], min(collisionvertex3f[3*e[1]+1], collisionvertex3f[3*e[2]+1])) - 1;
6434                         bihleafs[bihleafindex].mins[2] = min(collisionvertex3f[3*e[0]+2], min(collisionvertex3f[3*e[1]+2], collisionvertex3f[3*e[2]+2])) - 1;
6435                         bihleafs[bihleafindex].maxs[0] = max(collisionvertex3f[3*e[0]+0], max(collisionvertex3f[3*e[1]+0], collisionvertex3f[3*e[2]+0])) + 1;
6436                         bihleafs[bihleafindex].maxs[1] = max(collisionvertex3f[3*e[0]+1], max(collisionvertex3f[3*e[1]+1], collisionvertex3f[3*e[2]+1])) + 1;
6437                         bihleafs[bihleafindex].maxs[2] = max(collisionvertex3f[3*e[0]+2], max(collisionvertex3f[3*e[1]+2], collisionvertex3f[3*e[2]+2])) + 1;
6438                         bihleafindex++;
6439                 }
6440         }
6441
6442         // allocate buffers for the produced and temporary data
6443         bihnodes = Mem_Alloc(loadmodel->mempool, sizeof(bih_node_t) * bihmaxnodes);
6444         temp_leafsort = Mem_Alloc(loadmodel->mempool, sizeof(int) * bihnumleafs * 2);
6445         temp_leafsortscratch = temp_leafsort + bihnumleafs;
6446
6447         // now build it
6448         BIH_Build(&model->collision_bih, bihnumleafs, bihleafs, bihmaxnodes, bihnodes, temp_leafsort, temp_leafsortscratch);
6449
6450         // we're done with the temporary data
6451         Mem_Free(temp_leafsort);
6452
6453         // resize the BIH nodes array if it over-allocated
6454         if (model->collision_bih.maxnodes > model->collision_bih.numnodes)
6455         {
6456                 model->collision_bih.maxnodes = model->collision_bih.numnodes;
6457                 model->collision_bih.nodes = Mem_Realloc(loadmodel->mempool, model->collision_bih.nodes, model->collision_bih.numnodes * sizeof(bih_node_t));
6458         }
6459 }
6460
6461 static int Mod_Q3BSP_SuperContentsFromNativeContents(dp_model_t *model, int nativecontents)
6462 {
6463         int supercontents = 0;
6464         if (nativecontents & CONTENTSQ3_SOLID)
6465                 supercontents |= SUPERCONTENTS_SOLID;
6466         if (nativecontents & CONTENTSQ3_WATER)
6467                 supercontents |= SUPERCONTENTS_WATER;
6468         if (nativecontents & CONTENTSQ3_SLIME)
6469                 supercontents |= SUPERCONTENTS_SLIME;
6470         if (nativecontents & CONTENTSQ3_LAVA)
6471                 supercontents |= SUPERCONTENTS_LAVA;
6472         if (nativecontents & CONTENTSQ3_BODY)
6473                 supercontents |= SUPERCONTENTS_BODY;
6474         if (nativecontents & CONTENTSQ3_CORPSE)
6475                 supercontents |= SUPERCONTENTS_CORPSE;
6476         if (nativecontents & CONTENTSQ3_NODROP)
6477                 supercontents |= SUPERCONTENTS_NODROP;
6478         if (nativecontents & CONTENTSQ3_PLAYERCLIP)
6479                 supercontents |= SUPERCONTENTS_PLAYERCLIP;
6480         if (nativecontents & CONTENTSQ3_MONSTERCLIP)
6481                 supercontents |= SUPERCONTENTS_MONSTERCLIP;
6482         if (nativecontents & CONTENTSQ3_DONOTENTER)
6483                 supercontents |= SUPERCONTENTS_DONOTENTER;
6484         if (nativecontents & CONTENTSQ3_BOTCLIP)
6485                 supercontents |= SUPERCONTENTS_BOTCLIP;
6486         if (!(nativecontents & CONTENTSQ3_TRANSLUCENT))
6487                 supercontents |= SUPERCONTENTS_OPAQUE;
6488         return supercontents;
6489 }
6490
6491 static int Mod_Q3BSP_NativeContentsFromSuperContents(dp_model_t *model, int supercontents)
6492 {
6493         int nativecontents = 0;
6494         if (supercontents & SUPERCONTENTS_SOLID)
6495                 nativecontents |= CONTENTSQ3_SOLID;
6496         if (supercontents & SUPERCONTENTS_WATER)
6497                 nativecontents |= CONTENTSQ3_WATER;
6498         if (supercontents & SUPERCONTENTS_SLIME)
6499                 nativecontents |= CONTENTSQ3_SLIME;
6500         if (supercontents & SUPERCONTENTS_LAVA)
6501                 nativecontents |= CONTENTSQ3_LAVA;
6502         if (supercontents & SUPERCONTENTS_BODY)
6503                 nativecontents |= CONTENTSQ3_BODY;
6504         if (supercontents & SUPERCONTENTS_CORPSE)
6505                 nativecontents |= CONTENTSQ3_CORPSE;
6506         if (supercontents & SUPERCONTENTS_NODROP)
6507                 nativecontents |= CONTENTSQ3_NODROP;
6508         if (supercontents & SUPERCONTENTS_PLAYERCLIP)
6509                 nativecontents |= CONTENTSQ3_PLAYERCLIP;
6510         if (supercontents & SUPERCONTENTS_MONSTERCLIP)
6511                 nativecontents |= CONTENTSQ3_MONSTERCLIP;
6512         if (supercontents & SUPERCONTENTS_DONOTENTER)
6513                 nativecontents |= CONTENTSQ3_DONOTENTER;
6514         if (supercontents & SUPERCONTENTS_BOTCLIP)
6515                 nativecontents |= CONTENTSQ3_BOTCLIP;
6516         if (!(supercontents & SUPERCONTENTS_OPAQUE))
6517                 nativecontents |= CONTENTSQ3_TRANSLUCENT;
6518         return nativecontents;
6519 }
6520
6521 void Mod_Q3BSP_RecursiveFindNumLeafs(mnode_t *node)
6522 {
6523         int numleafs;
6524         while (node->plane)
6525         {
6526                 Mod_Q3BSP_RecursiveFindNumLeafs(node->children[0]);
6527                 node = node->children[1];
6528         }
6529         numleafs = ((mleaf_t *)node - loadmodel->brush.data_leafs) + 1;
6530         if (loadmodel->brush.num_leafs < numleafs)
6531                 loadmodel->brush.num_leafs = numleafs;
6532 }
6533
6534 void Mod_Q3BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
6535 {
6536         int i, j, numshadowmeshtriangles, lumps;
6537         q3dheader_t *header;
6538         float corner[3], yawradius, modelradius;
6539         msurface_t *surface;
6540
6541         mod->modeldatatypestring = "Q3BSP";
6542
6543         mod->type = mod_brushq3;
6544         mod->numframes = 2; // although alternate textures are not supported it is annoying to complain about no such frame 1
6545         mod->numskins = 1;
6546
6547         header = (q3dheader_t *)buffer;
6548         if((char *) bufferend < (char *) buffer + sizeof(q3dheader_t))
6549                 Host_Error("Mod_Q3BSP_Load: %s is smaller than its header", mod->name);
6550
6551         i = LittleLong(header->version);
6552         if (i != Q3BSPVERSION && i != Q3BSPVERSION_IG && i != Q3BSPVERSION_LIVE)
6553                 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
6554
6555         mod->soundfromcenter = true;
6556         mod->TraceBox = Mod_Q3BSP_TraceBox;
6557         mod->TraceLine = Mod_Q3BSP_TraceLine;
6558         mod->TracePoint = Mod_Q3BSP_TracePoint;
6559         mod->PointSuperContents = Mod_Q3BSP_PointSuperContents;
6560         mod->brush.TraceLineOfSight = Mod_Q3BSP_TraceLineOfSight;
6561         mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
6562         mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
6563         mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
6564         mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
6565         mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
6566         mod->brush.BoxTouchingLeafPVS = Mod_Q1BSP_BoxTouchingLeafPVS;
6567         mod->brush.BoxTouchingVisibleLeafs = Mod_Q1BSP_BoxTouchingVisibleLeafs;
6568         mod->brush.FindBoxClusters = Mod_Q1BSP_FindBoxClusters;
6569         mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
6570         mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
6571         mod->brush.AmbientSoundLevelsForPoint = NULL;
6572         mod->brush.RoundUpToHullSize = NULL;
6573         mod->brush.PointInLeaf = Mod_Q1BSP_PointInLeaf;
6574         mod->Draw = R_Q1BSP_Draw;
6575         mod->DrawDepth = R_Q1BSP_DrawDepth;
6576         mod->DrawDebug = R_Q1BSP_DrawDebug;
6577         mod->DrawPrepass = R_Q1BSP_DrawPrepass;
6578         mod->GetLightInfo = R_Q1BSP_GetLightInfo;
6579         mod->CompileShadowMap = R_Q1BSP_CompileShadowMap;
6580         mod->DrawShadowMap = R_Q1BSP_DrawShadowMap;
6581         mod->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
6582         mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
6583         mod->DrawLight = R_Q1BSP_DrawLight;
6584
6585         mod_base = (unsigned char *)header;
6586
6587         // swap all the lumps
6588         header->ident = LittleLong(header->ident);
6589         header->version = LittleLong(header->version);
6590         lumps = (header->version == Q3BSPVERSION_LIVE) ? Q3HEADER_LUMPS_LIVE : Q3HEADER_LUMPS;
6591         for (i = 0;i < lumps;i++)
6592         {
6593                 j = (header->lumps[i].fileofs = LittleLong(header->lumps[i].fileofs));
6594                 if((char *) bufferend < (char *) buffer + j)
6595                         Host_Error("Mod_Q3BSP_Load: %s has a lump that starts outside the file!", mod->name);
6596                 j += (header->lumps[i].filelen = LittleLong(header->lumps[i].filelen));
6597                 if((char *) bufferend < (char *) buffer + j)
6598                         Host_Error("Mod_Q3BSP_Load: %s has a lump that ends outside the file!", mod->name);
6599         }
6600         /*
6601          * NO, do NOT clear them!
6602          * they contain actual data referenced by other stuff.
6603          * Instead, before using the advertisements lump, check header->versio
6604          * again!
6605          * Sorry, but otherwise it breaks memory of the first lump.
6606         for (i = lumps;i < Q3HEADER_LUMPS_MAX;i++)
6607         {
6608                 header->lumps[i].fileofs = 0;
6609                 header->lumps[i].filelen = 0;
6610         }
6611         */
6612
6613         mod->brush.qw_md4sum = 0;
6614         mod->brush.qw_md4sum2 = 0;
6615         for (i = 0;i < lumps;i++)
6616         {
6617                 if (i == Q3LUMP_ENTITIES)
6618                         continue;
6619                 mod->brush.qw_md4sum ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen);
6620                 if (i == Q3LUMP_PVS || i == Q3LUMP_LEAFS || i == Q3LUMP_NODES)
6621                         continue;
6622                 mod->brush.qw_md4sum2 ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen);
6623
6624                 // all this checksumming can take a while, so let's send keepalives here too
6625                 CL_KeepaliveMessage(false);
6626         }
6627
6628         Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
6629         Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
6630         Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
6631         if (header->version == Q3BSPVERSION_IG)
6632                 Mod_Q3BSP_LoadBrushSides_IG(&header->lumps[Q3LUMP_BRUSHSIDES]);
6633         else
6634                 Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
6635         Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
6636         Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
6637         Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
6638         Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
6639         Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS], &header->lumps[Q3LUMP_FACES]);
6640         Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
6641         Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
6642         Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
6643         Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
6644         Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
6645         Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
6646         Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
6647         Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
6648         loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
6649
6650         // the MakePortals code works fine on the q3bsp data as well
6651         Mod_Q1BSP_MakePortals();
6652
6653         // FIXME: shader alpha should replace r_wateralpha support in q3bsp
6654         loadmodel->brush.supportwateralpha = true;
6655
6656         // make a single combined shadow mesh to allow optimized shadow volume creation
6657         numshadowmeshtriangles = 0;
6658         if (cls.state != ca_dedicated)
6659         {
6660                 for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
6661                 {
6662                         surface->num_firstshadowmeshtriangle = numshadowmeshtriangles;
6663                         numshadowmeshtriangles += surface->num_triangles;
6664                 }
6665                 loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
6666                 for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
6667                         if (surface->num_triangles > 0)
6668                                 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));
6669                 loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true, false);
6670                 if (loadmodel->brush.shadowmesh)
6671                         Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
6672         }
6673
6674         loadmodel->brush.num_leafs = 0;
6675         Mod_Q3BSP_RecursiveFindNumLeafs(loadmodel->brush.data_nodes);
6676
6677         if (loadmodel->brush.numsubmodels)
6678                 loadmodel->brush.submodels = (dp_model_t **)Mem_Alloc(loadmodel->mempool, loadmodel->brush.numsubmodels * sizeof(dp_model_t *));
6679
6680         mod = loadmodel;
6681         for (i = 0;i < loadmodel->brush.numsubmodels;i++)
6682         {
6683                 if (i > 0)
6684                 {
6685                         char name[10];
6686                         // duplicate the basic information
6687                         dpsnprintf(name, sizeof(name), "*%i", i);
6688                         mod = Mod_FindName(name, loadmodel->name);
6689                         // copy the base model to this one
6690                         *mod = *loadmodel;
6691                         // rename the clone back to its proper name
6692                         strlcpy(mod->name, name, sizeof(mod->name));
6693                         mod->brush.parentmodel = loadmodel;
6694                         // textures and memory belong to the main model
6695                         mod->texturepool = NULL;
6696                         mod->mempool = NULL;
6697                         mod->brush.GetPVS = NULL;
6698                         mod->brush.FatPVS = NULL;
6699                         mod->brush.BoxTouchingPVS = NULL;
6700                         mod->brush.BoxTouchingLeafPVS = NULL;
6701                         mod->brush.BoxTouchingVisibleLeafs = NULL;
6702                         mod->brush.FindBoxClusters = NULL;
6703                         mod->brush.LightPoint = NULL;
6704                         mod->brush.AmbientSoundLevelsForPoint = NULL;
6705                 }
6706                 mod->brush.submodel = i;
6707                 if (loadmodel->brush.submodels)
6708                         loadmodel->brush.submodels[i] = mod;
6709
6710                 // make the model surface list (used by shadowing/lighting)
6711                 mod->firstmodelsurface = mod->brushq3.data_models[i].firstface;
6712                 mod->nummodelsurfaces = mod->brushq3.data_models[i].numfaces;
6713                 mod->firstmodelbrush = mod->brushq3.data_models[i].firstbrush;
6714                 mod->nummodelbrushes = mod->brushq3.data_models[i].numbrushes;
6715                 mod->sortedmodelsurfaces = (int *)Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->sortedmodelsurfaces));
6716                 Mod_MakeSortedSurfaces(mod);
6717
6718                 VectorCopy(mod->brushq3.data_models[i].mins, mod->normalmins);
6719                 VectorCopy(mod->brushq3.data_models[i].maxs, mod->normalmaxs);
6720                 // enlarge the bounding box to enclose all geometry of this model,
6721                 // because q3map2 sometimes lies (mostly to affect the lightgrid),
6722                 // which can in turn mess up the farclip (as well as culling when
6723                 // outside the level - an unimportant concern)
6724
6725                 //printf("Editing model %d... BEFORE re-bounding: %f %f %f - %f %f %f\n", i, mod->normalmins[0], mod->normalmins[1], mod->normalmins[2], mod->normalmaxs[0], mod->normalmaxs[1], mod->normalmaxs[2]);
6726                 for (j = 0;j < mod->nummodelsurfaces;j++)
6727                 {
6728                         const msurface_t *surface = mod->data_surfaces + j + mod->firstmodelsurface;
6729                         const float *v = mod->surfmesh.data_vertex3f + 3 * surface->num_firstvertex;
6730                         int k;
6731                         if (!surface->num_vertices)
6732                                 continue;
6733                         for (k = 0;k < surface->num_vertices;k++, v += 3)
6734                         {
6735                                 mod->normalmins[0] = min(mod->normalmins[0], v[0]);
6736                                 mod->normalmins[1] = min(mod->normalmins[1], v[1]);
6737                                 mod->normalmins[2] = min(mod->normalmins[2], v[2]);
6738                                 mod->normalmaxs[0] = max(mod->normalmaxs[0], v[0]);
6739                                 mod->normalmaxs[1] = max(mod->normalmaxs[1], v[1]);
6740                                 mod->normalmaxs[2] = max(mod->normalmaxs[2], v[2]);
6741                         }
6742                 }
6743                 //printf("Editing model %d... AFTER re-bounding: %f %f %f - %f %f %f\n", i, mod->normalmins[0], mod->normalmins[1], mod->normalmins[2], mod->normalmaxs[0], mod->normalmaxs[1], mod->normalmaxs[2]);
6744                 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
6745                 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
6746                 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
6747                 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
6748                 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
6749                 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
6750                 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
6751                 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
6752                 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
6753                 mod->yawmins[2] = mod->normalmins[2];
6754                 mod->yawmaxs[2] = mod->normalmaxs[2];
6755                 mod->radius = modelradius;
6756                 mod->radius2 = modelradius * modelradius;
6757
6758                 // this gets altered below if sky or water is used
6759                 mod->DrawSky = NULL;
6760                 mod->DrawAddWaterPlanes = NULL;
6761
6762                 for (j = 0;j < mod->nummodelsurfaces;j++)
6763                         if (mod->data_surfaces[j + mod->firstmodelsurface].texture->basematerialflags & MATERIALFLAG_SKY)
6764                                 break;
6765                 if (j < mod->nummodelsurfaces)
6766                         mod->DrawSky = R_Q1BSP_DrawSky;
6767
6768                 for (j = 0;j < mod->nummodelsurfaces;j++)
6769                         if (mod->data_surfaces[j + mod->firstmodelsurface].texture->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION))
6770                                 break;
6771                 if (j < mod->nummodelsurfaces)
6772                         mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
6773
6774                 Mod_MakeCollisionData(mod);
6775
6776                 // generate VBOs and other shared data before cloning submodels
6777                 if (i == 0)
6778                         Mod_BuildVBOs();
6779         }
6780
6781         Con_DPrintf("Stats for q3bsp model \"%s\": %i faces, %i nodes, %i leafs, %i clusters, %i clusterportals, mesh: %i vertices, %i triangles, %i surfaces\n", loadmodel->name, loadmodel->num_surfaces, loadmodel->brush.num_nodes, loadmodel->brush.num_leafs, mod->brush.num_pvsclusters, loadmodel->brush.num_portals, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->num_surfaces);
6782 }
6783
6784 void Mod_IBSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
6785 {
6786         int i = LittleLong(((int *)buffer)[1]);
6787         if (i == Q3BSPVERSION || i == Q3BSPVERSION_IG || i == Q3BSPVERSION_LIVE)
6788                 Mod_Q3BSP_Load(mod,buffer, bufferend);
6789         else if (i == Q2BSPVERSION)
6790                 Mod_Q2BSP_Load(mod,buffer, bufferend);
6791         else
6792                 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i", i);
6793 }
6794
6795 void Mod_MAP_Load(dp_model_t *mod, void *buffer, void *bufferend)
6796 {
6797         Host_Error("Mod_MAP_Load: not yet implemented");
6798 }
6799
6800 #define OBJASMODEL
6801
6802 #ifdef OBJASMODEL
6803 typedef struct objvertex_s
6804 {
6805         int nextindex;
6806         int textureindex;
6807         float v[3];
6808         float vt[2];
6809         float vn[3];
6810 }
6811 objvertex_t;
6812
6813 void Mod_OBJ_Load(dp_model_t *mod, void *buffer, void *bufferend)
6814 {
6815         const char *textbase = (char *)buffer, *text = textbase;
6816         char *s;
6817         char *argv[512];
6818         char line[1024];
6819         char materialname[MAX_QPATH];
6820         int i, j, numvertices, firstvertex, firsttriangle, elementindex, vertexindex, numsurfaces, surfacevertices, surfacetriangles, surfaceelements;
6821         int index1, index2, index3;
6822         objvertex_t vfirst, vprev, vcurrent;
6823         int argc;
6824         int linelen;
6825         int numtriangles = 0;
6826         int maxtriangles = 0;
6827         objvertex_t *vertices = NULL;
6828         int linenumber = 0;
6829         int maxtextures = 0, numtextures = 0, textureindex = 0;
6830         int maxv = 0, numv = 1;
6831         int maxvt = 0, numvt = 1;
6832         int maxvn = 0, numvn = 1;
6833         char *texturenames = NULL;
6834         float dist, modelradius, modelyawradius;
6835         float *v = NULL;
6836         float *vt = NULL;
6837         float *vn = NULL;
6838         float mins[3];
6839         float maxs[3];
6840         objvertex_t *thisvertex = NULL;
6841         int vertexhashindex;
6842         int *vertexhashtable = NULL;
6843         objvertex_t *vertexhashdata = NULL;
6844         objvertex_t *vdata = NULL;
6845         int vertexhashsize = 0;
6846         int vertexhashcount = 0;
6847         skinfile_t *skinfiles = NULL;
6848         unsigned char *data = NULL;
6849
6850         memset(&vfirst, 0, sizeof(vfirst));
6851         memset(&vprev, 0, sizeof(vprev));
6852         memset(&vcurrent, 0, sizeof(vcurrent));
6853
6854         dpsnprintf(materialname, sizeof(materialname), "%s", loadmodel->name);
6855
6856         loadmodel->modeldatatypestring = "OBJ";
6857
6858         loadmodel->type = mod_obj;
6859         loadmodel->soundfromcenter = true;
6860         loadmodel->TraceBox = NULL;
6861         loadmodel->TraceLine = NULL;
6862         loadmodel->TracePoint = NULL;
6863         loadmodel->PointSuperContents = NULL;
6864         loadmodel->brush.TraceLineOfSight = NULL;
6865         loadmodel->brush.SuperContentsFromNativeContents = NULL;
6866         loadmodel->brush.NativeContentsFromSuperContents = NULL;
6867         loadmodel->brush.GetPVS = NULL;
6868         loadmodel->brush.FatPVS = NULL;
6869         loadmodel->brush.BoxTouchingPVS = NULL;
6870         loadmodel->brush.BoxTouchingLeafPVS = NULL;
6871         loadmodel->brush.BoxTouchingVisibleLeafs = NULL;
6872         loadmodel->brush.FindBoxClusters = NULL;
6873         loadmodel->brush.LightPoint = NULL;
6874         loadmodel->brush.FindNonSolidLocation = NULL;
6875         loadmodel->brush.AmbientSoundLevelsForPoint = NULL;
6876         loadmodel->brush.RoundUpToHullSize = NULL;
6877         loadmodel->brush.PointInLeaf = NULL;
6878         loadmodel->Draw = R_Q1BSP_Draw;
6879         loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
6880         loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
6881         loadmodel->DrawPrepass = R_Q1BSP_DrawPrepass;
6882         loadmodel->GetLightInfo = R_Q1BSP_GetLightInfo;
6883         loadmodel->CompileShadowMap = R_Q1BSP_CompileShadowMap;
6884         loadmodel->DrawShadowMap = R_Q1BSP_DrawShadowMap;
6885         loadmodel->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
6886         loadmodel->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
6887         loadmodel->DrawLight = R_Q1BSP_DrawLight;
6888
6889         skinfiles = Mod_LoadSkinFiles();
6890         if (loadmodel->numskins < 1)
6891                 loadmodel->numskins = 1;
6892
6893         // make skinscenes for the skins (no groups)
6894         loadmodel->skinscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, sizeof(animscene_t) * loadmodel->numskins);
6895         for (i = 0;i < loadmodel->numskins;i++)
6896         {
6897                 loadmodel->skinscenes[i].firstframe = i;
6898                 loadmodel->skinscenes[i].framecount = 1;
6899                 loadmodel->skinscenes[i].loop = true;
6900                 loadmodel->skinscenes[i].framerate = 10;
6901         }
6902
6903         VectorClear(mins);
6904         VectorClear(maxs);
6905
6906         // parse the OBJ text now
6907         for(;;)
6908         {
6909                 if (!*text)
6910                         break;
6911                 linenumber++;
6912                 linelen = 0;
6913                 for (linelen = 0;text[linelen] && text[linelen] != '\r' && text[linelen] != '\n';linelen++)
6914                         line[linelen] = text[linelen];
6915                 line[linelen] = 0;
6916                 for (argc = 0;argc < 4;argc++)
6917                         argv[argc] = "";
6918                 argc = 0;
6919                 s = line;
6920                 while (*s == ' ' || *s == '\t')
6921                         s++;
6922                 while (*s)
6923                 {
6924                         argv[argc++] = s;
6925                         while (*s > ' ')
6926                                 s++;
6927                         if (!*s)
6928                                 break;
6929                         *s++ = 0;
6930                         while (*s == ' ' || *s == '\t')
6931                                 s++;
6932                 }
6933                 text += linelen;
6934                 if (*text == '\r')
6935                         text++;
6936                 if (*text == '\n')
6937                         text++;
6938                 if (!argc)
6939                         continue;
6940                 if (argv[0][0] == '#')
6941                         continue;
6942                 if (!strcmp(argv[0], "v"))
6943                 {
6944                         if (maxv <= numv)
6945                         {
6946                                 maxv = max(maxv * 2, 1024);
6947                                 v = (float *)Mem_Realloc(tempmempool, v, maxv * sizeof(float[3]));
6948                         }
6949                         v[numv*3+0] = atof(argv[1]);
6950                         v[numv*3+2] = atof(argv[2]);
6951                         v[numv*3+1] = atof(argv[3]);
6952                         numv++;
6953                 }
6954                 else if (!strcmp(argv[0], "vt"))
6955                 {
6956                         if (maxvt <= numvt)
6957                         {
6958                                 maxvt = max(maxvt * 2, 1024);
6959                                 vt = (float *)Mem_Realloc(tempmempool, vt, maxvt * sizeof(float[2]));
6960                         }
6961                         vt[numvt*2+0] = atof(argv[1]);
6962                         vt[numvt*2+1] = 1-atof(argv[2]);
6963                         numvt++;
6964                 }
6965                 else if (!strcmp(argv[0], "vn"))
6966                 {
6967                         if (maxvn <= numvn)
6968                         {
6969                                 maxvn = max(maxvn * 2, 1024);
6970                                 vn = (float *)Mem_Realloc(tempmempool, vn, maxvn * sizeof(float[3]));
6971                         }
6972                         vn[numvn*3+0] = atof(argv[1]);
6973                         vn[numvn*3+2] = atof(argv[2]);
6974                         vn[numvn*3+1] = atof(argv[3]);
6975                         numvn++;
6976                 }
6977                 else if (!strcmp(argv[0], "f"))
6978                 {
6979                         if (!numtextures)
6980                         {
6981                                 if (maxtextures <= numtextures)
6982                                 {
6983                                         maxtextures = max(maxtextures * 2, 256);
6984                                         texturenames = (char *)Mem_Realloc(loadmodel->mempool, texturenames, maxtextures * MAX_QPATH);
6985                                 }
6986                                 textureindex = numtextures++;
6987                                 strlcpy(texturenames + textureindex*MAX_QPATH, loadmodel->name, MAX_QPATH);
6988                         }
6989                         for (j = 1;j < argc;j++)
6990                         {
6991                                 index1 = atoi(argv[j]);
6992                                 while(argv[j][0] && argv[j][0] != '/')
6993                                         argv[j]++;
6994                                 if (argv[j][0])
6995                                         argv[j]++;
6996                                 index2 = atoi(argv[j]);
6997                                 while(argv[j][0] && argv[j][0] != '/')
6998                                         argv[j]++;
6999                                 if (argv[j][0])
7000                                         argv[j]++;
7001                                 index3 = atoi(argv[j]);
7002                                 // negative refers to a recent vertex
7003                                 // zero means not specified
7004                                 // positive means an absolute vertex index
7005                                 if (index1 < 0)
7006                                         index1 = numv - index1;
7007                                 if (index2 < 0)
7008                                         index2 = numvt - index2;
7009                                 if (index3 < 0)
7010                                         index3 = numvn - index3;
7011                                 vcurrent.nextindex = -1;
7012                                 vcurrent.textureindex = textureindex;
7013                                 VectorCopy(v + 3*index1, vcurrent.v);
7014                                 Vector2Copy(vt + 2*index2, vcurrent.vt);
7015                                 VectorCopy(vn + 3*index3, vcurrent.vn);
7016                                 if (numtriangles == 0)
7017                                 {
7018                                         VectorCopy(vcurrent.v, mins);
7019                                         VectorCopy(vcurrent.v, maxs);
7020                                 }
7021                                 else
7022                                 {
7023                                         mins[0] = min(mins[0], vcurrent.v[0]);
7024                                         mins[1] = min(mins[1], vcurrent.v[1]);
7025                                         mins[2] = min(mins[2], vcurrent.v[2]);
7026                                         maxs[0] = max(maxs[0], vcurrent.v[0]);
7027                                         maxs[1] = max(maxs[1], vcurrent.v[1]);
7028                                         maxs[2] = max(maxs[2], vcurrent.v[2]);
7029                                 }
7030                                 if (j == 1)
7031                                         vfirst = vcurrent;
7032                                 else if (j >= 3)
7033                                 {
7034                                         if (maxtriangles <= numtriangles)
7035                                         {
7036                                                 maxtriangles = max(maxtriangles * 2, 32768);
7037                                                 vertices = (objvertex_t*)Mem_Realloc(loadmodel->mempool, vertices, maxtriangles * sizeof(objvertex_t[3]));
7038                                         }
7039                                         vertices[numtriangles*3+0] = vfirst;
7040                                         vertices[numtriangles*3+1] = vprev;
7041                                         vertices[numtriangles*3+2] = vcurrent;
7042                                         numtriangles++;
7043                                 }
7044                                 vprev = vcurrent;
7045                         }
7046                 }
7047                 else if (!strcmp(argv[0], "o") || !strcmp(argv[0], "g"))
7048                         ;
7049                 else if (!strcmp(argv[0], "usemtl"))
7050                 {
7051                         for (i = 0;i < numtextures;i++)
7052                                 if (!strcmp(texturenames+i*MAX_QPATH, argv[1]))
7053                                         break;
7054                         if (i < numtextures)
7055                                 textureindex = i;
7056                         else
7057                         {
7058                                 if (maxtextures <= numtextures)
7059                                 {
7060                                         maxtextures = max(maxtextures * 2, 256);
7061                                         texturenames = (char *)Mem_Realloc(loadmodel->mempool, texturenames, maxtextures * MAX_QPATH);
7062                                 }
7063                                 textureindex = numtextures++;
7064                                 strlcpy(texturenames + textureindex*MAX_QPATH, argv[1], MAX_QPATH);
7065                         }
7066                 }
7067         }
7068
7069         // now that we have the OBJ data loaded as-is, we can convert it
7070
7071         // copy the model bounds, then enlarge the yaw and rotated bounds according to radius
7072         VectorCopy(mins, loadmodel->normalmins);
7073         VectorCopy(maxs, loadmodel->normalmaxs);
7074         dist = max(fabs(loadmodel->normalmins[0]), fabs(loadmodel->normalmaxs[0]));
7075         modelyawradius = max(fabs(loadmodel->normalmins[1]), fabs(loadmodel->normalmaxs[1]));
7076         modelyawradius = dist*dist+modelyawradius*modelyawradius;
7077         modelradius = max(fabs(loadmodel->normalmins[2]), fabs(loadmodel->normalmaxs[2]));
7078         modelradius = modelyawradius + modelradius * modelradius;
7079         modelyawradius = sqrt(modelyawradius);
7080         modelradius = sqrt(modelradius);
7081         loadmodel->yawmins[0] = loadmodel->yawmins[1] = -modelyawradius;
7082         loadmodel->yawmins[2] = loadmodel->normalmins[2];
7083         loadmodel->yawmaxs[0] = loadmodel->yawmaxs[1] =  modelyawradius;
7084         loadmodel->yawmaxs[2] = loadmodel->normalmaxs[2];
7085         loadmodel->rotatedmins[0] = loadmodel->rotatedmins[1] = loadmodel->rotatedmins[2] = -modelradius;
7086         loadmodel->rotatedmaxs[0] = loadmodel->rotatedmaxs[1] = loadmodel->rotatedmaxs[2] =  modelradius;
7087         loadmodel->radius = modelradius;
7088         loadmodel->radius2 = modelradius * modelradius;
7089
7090         // allocate storage for triangles
7091         loadmodel->num_surfaces = loadmodel->nummodelsurfaces = numsurfaces = numtextures;
7092         loadmodel->surfmesh.data_element3i = Mem_Alloc(loadmodel->mempool, numtriangles * sizeof(int[3]));
7093         loadmodel->data_surfaces = (msurface_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t));
7094         // allocate vertex hash structures to build an optimal vertex subset
7095         vertexhashsize = numtriangles*2;
7096         vertexhashtable = Mem_Alloc(loadmodel->mempool, sizeof(int) * vertexhashsize);
7097         memset(vertexhashtable, 0xFF, sizeof(int) * vertexhashsize);
7098         vertexhashdata = Mem_Alloc(loadmodel->mempool, sizeof(*vertexhashdata) * numtriangles*3);
7099         vertexhashcount = 0;
7100
7101         // gather surface stats for assigning vertex/triangle ranges
7102         firstvertex = 0;
7103         firsttriangle = 0;
7104         elementindex = 0;
7105         for (textureindex = 0;textureindex < numtextures;textureindex++)
7106         {
7107                 msurface_t *surface = loadmodel->data_surfaces + textureindex;
7108                 // copy the mins/maxs of the model backwards so that the first vertex
7109                 // added will set the surface bounds to a point
7110                 VectorCopy(loadmodel->normalmaxs, surface->mins);
7111                 VectorCopy(loadmodel->normalmins, surface->maxs);
7112                 surfacevertices = 0;
7113                 surfaceelements = 0;
7114                 for (vertexindex = 0;vertexindex < numtriangles*3;vertexindex++)
7115                 {
7116                         thisvertex = vertices + vertexindex;
7117                         if (thisvertex->textureindex != textureindex)
7118                                 continue;
7119                         surface->mins[0] = min(surface->mins[0], thisvertex->v[0]);
7120                         surface->mins[1] = min(surface->mins[1], thisvertex->v[1]);
7121                         surface->mins[2] = min(surface->mins[2], thisvertex->v[2]);
7122                         surface->maxs[0] = max(surface->maxs[0], thisvertex->v[0]);
7123                         surface->maxs[1] = max(surface->maxs[1], thisvertex->v[1]);
7124                         surface->maxs[2] = max(surface->maxs[2], thisvertex->v[2]);
7125                         vertexhashindex = (unsigned int)(thisvertex->v[0] * 3571 + thisvertex->v[0] * 1777 + thisvertex->v[0] * 457) % (unsigned int)vertexhashsize;
7126                         for (i = vertexhashtable[vertexhashindex];i >= 0;i = vertexhashdata[i].nextindex)
7127                         {
7128                                 vdata = vertexhashdata + i;
7129                                 if (vdata->textureindex == thisvertex->textureindex && VectorCompare(thisvertex->v, vdata->v) && VectorCompare(thisvertex->vn, vdata->vn) && Vector2Compare(thisvertex->vt, vdata->vt))
7130                                         break;
7131                         }
7132                         if (i < 0)
7133                         {
7134                                 i = vertexhashcount++;
7135                                 vdata = vertexhashdata + i;
7136                                 *vdata = *thisvertex;
7137                                 vdata->nextindex = vertexhashtable[vertexhashindex];
7138                                 vertexhashtable[vertexhashindex] = i;
7139                                 surfacevertices++;
7140                         }
7141                         loadmodel->surfmesh.data_element3i[elementindex++] = i;
7142                         surfaceelements++;
7143                 }
7144                 surfacetriangles = surfaceelements / 3;
7145                 surface->num_vertices = surfacevertices;
7146                 surface->num_triangles = surfacetriangles;
7147                 surface->num_firstvertex = firstvertex;
7148                 surface->num_firsttriangle = firsttriangle;
7149                 firstvertex += surface->num_vertices;
7150                 firsttriangle += surface->num_triangles;
7151         }
7152         numvertices = firstvertex;
7153
7154         // allocate storage for final mesh data
7155         loadmodel->num_textures = numtextures * loadmodel->numskins;
7156         loadmodel->num_texturesperskin = numtextures;
7157         data = (unsigned char *)Mem_Alloc(loadmodel->mempool, numsurfaces * sizeof(int) + numsurfaces * loadmodel->numskins * sizeof(texture_t) + numtriangles * sizeof(int[3]) + (numvertices <= 65536 ? numtriangles * sizeof(unsigned short[3]) : 0) + numvertices * sizeof(float[14]));
7158         loadmodel->sortedmodelsurfaces = (int *)data;data += numsurfaces * sizeof(int);
7159         loadmodel->data_textures = (texture_t *)data;data += numsurfaces * loadmodel->numskins * sizeof(texture_t);
7160         loadmodel->surfmesh.num_vertices = numvertices;
7161         loadmodel->surfmesh.num_triangles = numtriangles;
7162         loadmodel->surfmesh.data_neighbor3i = (int *)data;data += numtriangles * sizeof(int[3]);
7163         loadmodel->surfmesh.data_vertex3f = (float *)data;data += numvertices * sizeof(float[3]);
7164         loadmodel->surfmesh.data_svector3f = (float *)data;data += numvertices * sizeof(float[3]);
7165         loadmodel->surfmesh.data_tvector3f = (float *)data;data += numvertices * sizeof(float[3]);
7166         loadmodel->surfmesh.data_normal3f = (float *)data;data += numvertices * sizeof(float[3]);
7167         loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += numvertices * sizeof(float[2]);
7168         if (loadmodel->surfmesh.num_vertices <= 65536)
7169                 loadmodel->surfmesh.data_element3s = (unsigned short *)data;data += loadmodel->surfmesh.num_triangles * sizeof(unsigned short[3]);
7170
7171         for (j = 0;j < loadmodel->surfmesh.num_vertices;j++)
7172         {
7173                 VectorCopy(vertexhashdata[j].v, loadmodel->surfmesh.data_vertex3f + 3*j);
7174                 VectorCopy(vertexhashdata[j].vn, loadmodel->surfmesh.data_normal3f + 3*j);
7175                 Vector2Copy(vertexhashdata[j].vt, loadmodel->surfmesh.data_texcoordtexture2f + 2*j);
7176         }
7177
7178         // load the textures
7179         for (textureindex = 0;textureindex < numtextures;textureindex++)
7180                 Mod_BuildAliasSkinsFromSkinFiles(loadmodel->data_textures + textureindex, skinfiles, texturenames + textureindex*MAX_QPATH, texturenames + textureindex*MAX_QPATH);
7181         Mod_FreeSkinFiles(skinfiles);
7182
7183         // set the surface textures
7184         for (textureindex = 0;textureindex < numtextures;textureindex++)
7185         {
7186                 msurface_t *surface = loadmodel->data_surfaces + textureindex;
7187                 surface->texture = loadmodel->data_textures + textureindex;
7188         }
7189
7190         // free data
7191         Mem_Free(vertices);
7192         Mem_Free(texturenames);
7193         Mem_Free(v);
7194         Mem_Free(vt);
7195         Mem_Free(vn);
7196         Mem_Free(vertexhashtable);
7197         Mem_Free(vertexhashdata);
7198
7199         // compute all the mesh information that was not loaded from the file
7200         Mod_MakeSortedSurfaces(loadmodel);
7201         if (loadmodel->surfmesh.data_element3s)
7202                 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
7203                         loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
7204         Mod_ValidateElements(loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles, 0, loadmodel->surfmesh.num_vertices, __FILE__, __LINE__);
7205         // generate normals if the file did not have them
7206         if (!VectorLength2(loadmodel->surfmesh.data_normal3f))
7207                 Mod_BuildNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_normal3f, true);
7208         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);
7209         Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
7210 }
7211
7212
7213
7214
7215
7216
7217
7218
7219
7220
7221 #else // OBJASMODEL
7222
7223 #ifdef OBJWORKS
7224 typedef struct objvertex_s
7225 {
7226         float v[3];
7227         float vt[2];
7228         float vn[3];
7229 }
7230 objvertex_t;
7231
7232 typedef struct objtriangle_s
7233 {
7234         objvertex_t vertex[3];
7235         int textureindex;
7236         // these fields are used only in conversion to surfaces
7237         int axis;
7238         int surfaceindex;
7239         int surfacevertexindex[3];
7240         float edgeplane[3][4];
7241 }
7242 objtriangle_t;
7243
7244 typedef objnode_s
7245 {
7246         struct objnode_s *children[2];
7247         struct objnode_s *parent;
7248         objtriangle_t *triangles;
7249         float normal[3];
7250         float dist;
7251         float mins[3];
7252         float maxs[3];
7253         int numtriangles;
7254 }
7255 objnode_t;
7256
7257 objnode_t *Mod_OBJ_BSPNodeForTriangles(objnode_t *parent, objtriangle_t *triangles, int numtriangles, const float *mins, const float *maxs, mem_expandablearray_t *nodesarray, int maxclippedtriangles, objtriangle_t *clippedfronttriangles, objtriangle_t *clippedbacktriangles)
7258 {
7259         int i, j;
7260         float normal[3];
7261         float dist;
7262         int score;
7263         float bestnormal[3];
7264         float bestdist;
7265         int bestscore;
7266         float mins[3];
7267         float maxs[3];
7268         int numfronttriangles;
7269         int numbacktriangles;
7270         int count_front;
7271         int count_back;
7272         int count_both;
7273         int count_on;
7274         float outfrontpoints[5][3];
7275         float outbackpoints[5][3];
7276         int neededfrontpoints;
7277         int neededbackpoints;
7278         int countonpoints;
7279         objnode_t *node;
7280
7281         node = (objnode_t *)Mem_ExpandableArray_AllocRecord(array);
7282         node->parent = parent;
7283         if (numtriangles)
7284         {
7285                 VectorCopy(triangles[0].vertex[0].v, mins);
7286                 VectorCopy(triangles[0].vertex[0].v, maxs);
7287         }
7288         else if (parent && parent->children[0] == node)
7289         {
7290                 VectorCopy(parent->mins, mins);
7291                 Vectorcopy(parent->maxs, maxs);
7292         }
7293         else if (parent && parent->children[1] == node)
7294         {
7295                 VectorCopy(parent->mins, mins);
7296                 Vectorcopy(parent->maxs, maxs);
7297         }
7298         else
7299         {
7300                 VectorClear(mins);
7301                 VectorClear(maxs);
7302         }
7303         for (i = 0;i < numtriangles;i++)
7304         {
7305                 for (j = 0;j < 3;j++)
7306                 {
7307                         mins[0] = min(mins[0], triangles[i].vertex[j].v[0]);
7308                         mins[1] = min(mins[1], triangles[i].vertex[j].v[1]);
7309                         mins[2] = min(mins[2], triangles[i].vertex[j].v[2]);
7310                         maxs[0] = max(maxs[0], triangles[i].vertex[j].v[0]);
7311                         maxs[1] = max(maxs[1], triangles[i].vertex[j].v[1]);
7312                         maxs[2] = max(maxs[2], triangles[i].vertex[j].v[2]);
7313                 }
7314         }
7315         VectorCopy(mins, node->mins);
7316         VectorCopy(maxs, node->maxs);
7317         if (numtriangles <= mod_obj_leaftriangles.integer)
7318         {
7319                 // create a leaf
7320                 loadmodel->brush.num_leafs++;
7321                 node->triangles = triangles;
7322                 node->numtriangles = numtriangles;
7323                 return node;
7324         }
7325
7326         // create a node
7327         loadmodel->brush.num_nodes++;
7328         // pick a splitting plane from the various choices available to us...
7329         // early splits simply halve the interval
7330         bestscore = 0;
7331         VectorClear(bestnormal);
7332         bestdist = 0;
7333         if (numtriangles <= mod_obj_splitterlimit.integer)
7334                 limit = numtriangles;
7335         else
7336                 limit = 0;
7337         for (i = -3;i < limit;i++)
7338         {
7339                 if (i < 0)
7340                 {
7341                         // first we try 3 axial splits (kdtree-like)
7342                         j = i + 3;
7343                         VectorClear(normal);
7344                         normal[j] = 1;
7345                         dist = (mins[j] + maxs[j]) * 0.5f;
7346                 }
7347                 else
7348                 {
7349                         // then we try each triangle plane
7350                         TriangleNormal(triangles[i].vertex[0].v, triangles[i].vertex[1].v, triangles[i].vertex[2].v, normal);
7351                         VectorNormalize(normal);
7352                         dist = DotProduct(normal, triangles[i].vertex[0].v);
7353                         // use positive axial values whenever possible
7354                         if (normal[0] == -1)
7355                                 normal[0] = 1;
7356                         if (normal[1] == -1)
7357                                 normal[1] = 1;
7358                         if (normal[2] == -1)
7359                                 normal[2] = 1;
7360                         // skip planes that match the current best
7361                         if (VectorCompare(normal, bestnormal) && dist == bestdist)
7362                                 continue;
7363                 }
7364                 count_on = 0;
7365                 count_front = 0;
7366                 count_back = 0;
7367                 count_both = 0;
7368                 for (j = 0;j < numtriangles;j++)
7369                 {
7370                         dists[0] = DotProduct(normal, triangles[j].vertex[0].v) - dist;
7371                         dists[1] = DotProduct(normal, triangles[j].vertex[1].v) - dist;
7372                         dists[2] = DotProduct(normal, triangles[j].vertex[2].v) - dist;
7373                         if (dists[0] < -DIST_EPSILON || dists[1] < -DIST_EPSILON || dists[2] < -DIST_EPSILON)
7374                         {
7375                                 if (dists[0] > DIST_EPSILON || dists[1] > DIST_EPSILON || dists[2] > DIST_EPSILON)
7376                                         count_both++;
7377                                 else
7378                                         count_back++;
7379                         }
7380                         else if (dists[0] > DIST_EPSILON || dists[1] > DIST_EPSILON || dists[2] > DIST_EPSILON)
7381                                 count_front++;
7382                         else
7383                                 count_on++;
7384                 }
7385                 // score is supposed to:
7386                 // prefer axial splits
7387                 // prefer evenly dividing the input triangles
7388                 // prefer triangles on the plane
7389                 // avoid triangles crossing the plane
7390                 score = count_on*count_on - count_both*count_both + min(count_front, count_back)*(count_front+count_back);
7391                 if (normal[0] == 1 || normal[1] == 1 || normal[2] == 1)
7392                         score *= 2;
7393                 if (i == -3 || bestscore < score)
7394                 {
7395                         VectorCopy(normal, bestnormal);
7396                         bestdist = dist;
7397                         bestscore = score;
7398                 }
7399         }
7400
7401         // now we have chosen an optimal split plane...
7402
7403         // divide triangles by the splitting plane
7404         numfronttriangles = 0;
7405         numbacktriangles = 0;
7406         for (i = 0;i < numtriangles;i++)
7407         {
7408                 neededfrontpoints = 0;
7409                 neededbackpoints = 0;
7410                 countonpoints = 0;
7411                 PolygonF_Divide(3, triangles[i].vertex[0].v, bestnormal[0], bestnormal[1], bestnormal[2], bestdist, DIST_EPSILON, 5, outfrontpoints[0], &neededfrontpoints, 5, outbackpoints[0], &neededbackpoints, &countonpoints);
7412                 if (countonpoints > 1)
7413                 {
7414                         // triangle lies on plane, assign it to one child only
7415                         TriangleNormal(triangles[i].vertex[0].v, triangles[i].vertex[1].v, triangles[i].vertex[2].v, normal);
7416                         if (DotProduct(bestnormal, normal) >= 0)
7417                         {
7418                                 // assign to front side child
7419                                 obj_fronttriangles[numfronttriangles++] = triangles[i];
7420                         }
7421                         else
7422                         {
7423                                 // assign to back side child
7424                                 obj_backtriangles[numbacktriangles++] = triangles[i];
7425                         }
7426                 }
7427                 else
7428                 {
7429                         // convert clipped polygons to triangles
7430                         for (j = 0;j < neededfrontpoints-2;j++)
7431                         {
7432                                 obj_fronttriangles[numfronttriangles] = triangles[i];
7433                                 VectorCopy(outfrontpoints[0], obj_fronttriangles[numfronttriangles].vertex[0].v);
7434                                 VectorCopy(outfrontpoints[j+1], obj_fronttriangles[numfronttriangles].vertex[1].v);
7435                                 VectorCopy(outfrontpoints[j+2], obj_fronttriangles[numfronttriangles].vertex[2].v);
7436                                 numfronttriangles++;
7437                         }
7438                         for (j = 0;j < neededbackpoints-2;j++)
7439                         {
7440                                 obj_backtriangles[numbacktriangles] = triangles[i];
7441                                 VectorCopy(outbackpoints[0], obj_backtriangles[numbacktriangles].vertex[0].v);
7442                                 VectorCopy(outbackpoints[j+1], obj_backtriangles[numbacktriangles].vertex[1].v);
7443                                 VectorCopy(outbackpoints[j+2], obj_backtriangles[numbacktriangles].vertex[2].v);
7444                                 numbacktriangles++;
7445                         }
7446                 }
7447         }
7448
7449         // now copy the triangles out of the big buffer
7450         if (numfronttriangles)
7451         {
7452                 fronttriangles = Mem_Alloc(loadmodel->mempool, fronttriangles * sizeof(*fronttriangles));
7453                 memcpy(fronttriangles, obj_fronttriangles, numfronttriangles * sizeof(*fronttriangles));
7454         }
7455         else
7456                 fronttriangles = NULL;
7457         if (numbacktriangles)
7458         {
7459                 backtriangles = Mem_Alloc(loadmodel->mempool, backtriangles * sizeof(*backtriangles));
7460                 memcpy(backtriangles, obj_backtriangles, numbacktriangles * sizeof(*backtriangles));
7461         }
7462         else
7463                 backtriangles = NULL;
7464
7465         // free the original triangles we were given
7466         if (triangles)
7467                 Mem_Free(triangles);
7468         triangles = NULL;
7469         numtriangles = 0;
7470
7471         // now create the children...
7472         node->children[0] = Mod_OBJ_BSPNodeForTriangles(node, fronttriangles, numfronttriangles, frontmins, frontmaxs, nodesarray, maxclippedtriangles, clippedfronttriangles, clippedbacktriangles);
7473         node->children[1] = Mod_OBJ_BSPNodeForTriangles(node, backtriangles, numbacktriangles, backmins, backmaxs, nodesarray, maxclippedtriangles, clippedfronttriangles, clippedbacktriangles);
7474         return node;
7475 }
7476
7477 void Mod_OBJ_SnapVertex(float *v)
7478 {
7479         int i;
7480         float a = mod_obj_vertexprecision.value;
7481         float b = 1.0f / a;
7482         v[0] -= floor(v[0] * a + 0.5f) * b;
7483         v[1] -= floor(v[1] * a + 0.5f) * b;
7484         v[2] -= floor(v[2] * a + 0.5f) * b;
7485 }
7486
7487 void Mod_OBJ_ConvertBSPNode(objnode_t *objnode, mnode_t *mnodeparent)
7488 {
7489         if (objnode->children[0])
7490         {
7491                 // convert to mnode_t
7492                 mnode_t *mnode = loadmodel->brush.data_nodes + loadmodel->brush.num_nodes++;
7493                 mnode->parent = mnodeparent;
7494                 mnode->plane = loadmodel->brush.data_planes + loadmodel->brush.num_planes++;
7495                 VectorCopy(objnode->normal, mnode->plane->normal);
7496                 mnode->plane->dist = objnode->dist;
7497                 PlaneClassify(mnode->plane);
7498                 VectorCopy(objnode->mins, mnode->mins);
7499                 VectorCopy(objnode->maxs, mnode->maxs);
7500                 // push combinedsupercontents up to the parent
7501                 if (mnodeparent)
7502                         mnodeparent->combinedsupercontents |= mnode->combinedsupercontents;
7503                 mnode->children[0] = Mod_OBJ_ConvertBSPNode(objnode->children[0], mnode);
7504                 mnode->children[1] = Mod_OBJ_ConvertBSPNode(objnode->children[1], mnode);
7505         }
7506         else
7507         {
7508                 // convert to mleaf_t
7509                 mleaf_t *mleaf = loadmodel->brush.data_leafs + loadmodel->brush.num_leafs++;
7510                 mleaf->parent = mnodeparent;
7511                 VectorCopy(objnode->mins, mleaf->mins);
7512                 VectorCopy(objnode->maxs, mleaf->maxs);
7513                 mleaf->clusterindex = loadmodel->brush.num_leafs - 1;
7514                 if (objnode->numtriangles)
7515                 {
7516                         objtriangle_t *triangles = objnode->triangles;
7517                         int numtriangles = objnode->numtriangles;
7518                         texture_t *texture;
7519                         float edge[3][3];
7520                         float normal[3];
7521                         objvertex_t vertex[3];
7522                         numsurfaces = 0;
7523                         maxsurfaces = numtriangles;
7524                         surfaces = NULL;
7525                         // calculate some more data on each triangle for surface gathering
7526                         for (i = 0;i < numtriangles;i++)
7527                         {
7528                                 triangle = triangles + i;
7529                                 texture = loadmodel->data_textures + triangle->textureindex;
7530                                 Mod_OBJ_SnapVertex(triangle->vertex[0].v);
7531                                 Mod_OBJ_SnapVertex(triangle->vertex[1].v);
7532                                 Mod_OBJ_SnapVertex(triangle->vertex[2].v);
7533                                 TriangleNormal(triangle->vertex[0].v, triangle->vertex[1].v, triangle->vertex[2].v, normal);
7534                                 axis = 0;
7535                                 if (fabs(normal[axis]) < fabs(normal[1]))
7536                                         axis = 1;
7537                                 if (fabs(normal[axis]) < fabs(normal[2]))
7538                                         axis = 2;
7539                                 VectorClear(normal);
7540                                 normal[axis] = 1;
7541                                 triangle->axis = axis;
7542                                 VectorSubtract(triangle->vertex[1].v, triangle->vertex[0].v, edge[0]);
7543                                 VectorSubtract(triangle->vertex[2].v, triangle->vertex[1].v, edge[1]);
7544                                 VectorSubtract(triangle->vertex[0].v, triangle->vertex[2].v, edge[2]);
7545                                 CrossProduct(edge[0], normal, triangle->edgeplane[0]);
7546                                 CrossProduct(edge[1], normal, triangle->edgeplane[1]);
7547                                 CrossProduct(edge[2], normal, triangle->edgeplane[2]);
7548                                 VectorNormalize(triangle->edgeplane[0]);
7549                                 VectorNormalize(triangle->edgeplane[1]);
7550                                 VectorNormalize(triangle->edgeplane[2]);
7551                                 triangle->edgeplane[0][3] = DotProduct(triangle->edgeplane[0], triangle->vertex[0].v);
7552                                 triangle->edgeplane[1][3] = DotProduct(triangle->edgeplane[1], triangle->vertex[1].v);
7553                                 triangle->edgeplane[2][3] = DotProduct(triangle->edgeplane[2], triangle->vertex[2].v);
7554                                 triangle->surfaceindex = 0;
7555                                 // add to the combined supercontents while we're here...
7556                                 mleaf->combinedsupercontents |= texture->supercontents;
7557                         }
7558                         surfaceindex = 1;
7559                         for (i = 0;i < numtriangles;i++)
7560                         {
7561                                 // skip already-assigned triangles
7562                                 if (triangles[i].surfaceindex)
7563                                         continue;
7564                                 texture = loadmodel->data_textures + triangles[i].textureindex;
7565                                 // assign a new surface to this triangle
7566                                 triangles[i].surfaceindex = surfaceindex++;
7567                                 axis = triangles[i].axis;
7568                                 numvertices = 3;
7569                                 // find the triangle's neighbors, this can take multiple passes
7570                                 retry = true;
7571                                 while (retry)
7572                                 {
7573                                         retry = false;
7574                                         for (j = i+1;j < numtriangles;j++)
7575                                         {
7576                                                 if (triangles[j].surfaceindex || triangles[j].axis != axis || triangles[j].texture != texture)
7577                                                         continue;
7578                                                 triangle = triangles + j;
7579                                                 for (k = i;k < j;k++)
7580                                                 {
7581                                                         if (triangles[k].surfaceindex != surfaceindex)
7582                                                                 continue;
7583                                                         if (VectorCompare(triangles[k].vertex[0].v, triangles[j].vertex[0].v)
7584                                                          || VectorCompare(triangles[k].vertex[0].v, triangles[j].vertex[1].v)
7585                                                          || VectorCompare(triangles[k].vertex[0].v, triangles[j].vertex[2].v)
7586                                                          || VectorCompare(triangles[k].vertex[1].v, triangles[j].vertex[0].v)
7587                                                          || VectorCompare(triangles[k].vertex[1].v, triangles[j].vertex[1].v)
7588                                                          || VectorCompare(triangles[k].vertex[1].v, triangles[j].vertex[2].v)
7589                                                          || VectorCompare(triangles[k].vertex[2].v, triangles[j].vertex[0].v)
7590                                                          || VectorCompare(triangles[k].vertex[2].v, triangles[j].vertex[1].v)
7591                                                          || VectorCompare(triangles[k].vertex[2].v, triangles[j].vertex[2].v))
7592                                                         {
7593                                                                 // shares a vertex position
7594                                                                 --- FIXME ---
7595                                                         }
7596                                                 }
7597                                                 for (k = 0;k < numvertices;k++)
7598                                                         if (!VectorCompare(vertex[k].v, triangles[j].vertex[0].v) || !VectorCompare(vertex[k].v, triangles[j].vertex[1].v) || !VectorCompare(vertex[k].v, triangles[j].vertex[2].v))
7599                                                                 break;
7600                                                 if (k == numvertices)
7601                                                         break; // not a neighbor
7602                                                 // this triangle is a neighbor and has the same axis and texture
7603                                                 // check now if it overlaps in lightmap projection space
7604                                                 triangles[j].surfaceindex;
7605                                                 if (triangles[j].
7606                                         }
7607                                 }
7608                                 //triangles[i].surfaceindex = surfaceindex++;
7609                                 for (surfaceindex = 0;surfaceindex < numsurfaces;surfaceindex++)
7610                                 {
7611                                         if (surfaces[surfaceindex].texture != texture)
7612                                                 continue;
7613                                         // check if any triangles already in this surface overlap in lightmap projection space
7614                                         
7615                                         {
7616                                         }
7617                                         break;
7618                                 }
7619                         }
7620                         // let the collision code simply use the surfaces
7621                         mleaf->containscollisionsurfaces = mleaf->combinedsupercontents != 0;
7622                         mleaf->numleafsurfaces = ?;
7623                         mleaf->firstleafsurface = ?;
7624                 }
7625                 // push combinedsupercontents up to the parent
7626                 if (mnodeparent)
7627                         mnodeparent->combinedsupercontents |= mleaf->combinedsupercontents;
7628         }
7629 }
7630 #endif
7631
7632 void Mod_OBJ_Load(dp_model_t *mod, void *buffer, void *bufferend)
7633 {
7634 #ifdef OBJWORKS
7635         const char *textbase = (char *)buffer, *text = textbase;
7636         char *s;
7637         char *argv[512];
7638         char line[1024];
7639         char materialname[MAX_QPATH];
7640         int j, index1, index2, index3, first, prev, index;
7641         int argc;
7642         int linelen;
7643         int numtriangles = 0;
7644         int maxtriangles = 131072;
7645         objtriangle_t *triangles = Mem_Alloc(tempmempool, maxtriangles * sizeof(*triangles));
7646         int linenumber = 0;
7647         int maxtextures = 256, numtextures = 0, textureindex = 0;
7648         int maxv = 1024, numv = 0;
7649         int maxvt = 1024, numvt = 0;
7650         int maxvn = 1024, numvn = 0;
7651         char **texturenames;
7652         float *v = Mem_Alloc(tempmempool, maxv * sizeof(float[3]));
7653         float *vt = Mem_Alloc(tempmempool, maxvt * sizeof(float[2]));
7654         float *vn = Mem_Alloc(tempmempool, maxvn * sizeof(float[3]));
7655         objvertex_t vfirst, vprev, vcurrent;
7656         float mins[3];
7657         float maxs[3];
7658 #if 0
7659         int hashindex;
7660         int maxverthash = 65536, numverthash = 0;
7661         int numhashindex = 65536;
7662         struct objverthash_s
7663         {
7664                 struct objverthash_s *next;
7665                 int s;
7666                 int v;
7667                 int vt;
7668                 int vn;
7669         }
7670         *hash, **verthash = Mem_Alloc(tempmempool, numhashindex * sizeof(*verthash)), *verthashdata = Mem_Alloc(tempmempool, maxverthash * sizeof(*verthashdata)), *oldverthashdata;
7671 #endif
7672
7673         dpsnprintf(materialname, sizeof(materialname), "%s", loadmodel->name);
7674
7675         loadmodel->modeldatatypestring = "OBJ";
7676
7677         loadmodel->type = mod_obj;
7678         loadmodel->soundfromcenter = true;
7679         loadmodel->TraceBox = Mod_OBJ_TraceBox;
7680         loadmodel->TraceLine = Mod_OBJ_TraceLine;
7681         loadmodel->TracePoint = Mod_OBJ_TracePoint;
7682         loadmodel->PointSuperContents = Mod_OBJ_PointSuperContents;
7683         loadmodel->brush.TraceLineOfSight = Mod_OBJ_TraceLineOfSight;
7684         loadmodel->brush.SuperContentsFromNativeContents = Mod_OBJ_SuperContentsFromNativeContents;
7685         loadmodel->brush.NativeContentsFromSuperContents = Mod_OBJ_NativeContentsFromSuperContents;
7686         loadmodel->brush.GetPVS = Mod_OBJ_GetPVS;
7687         loadmodel->brush.FatPVS = Mod_OBJ_FatPVS;
7688         loadmodel->brush.BoxTouchingPVS = Mod_OBJ_BoxTouchingPVS;
7689         loadmodel->brush.BoxTouchingLeafPVS = Mod_OBJ_BoxTouchingLeafPVS;
7690         loadmodel->brush.BoxTouchingVisibleLeafs = Mod_OBJ_BoxTouchingVisibleLeafs;
7691         loadmodel->brush.FindBoxClusters = Mod_OBJ_FindBoxClusters;
7692         loadmodel->brush.LightPoint = Mod_OBJ_LightPoint;
7693         loadmodel->brush.FindNonSolidLocation = Mod_OBJ_FindNonSolidLocation;
7694         loadmodel->brush.AmbientSoundLevelsForPoint = NULL;
7695         loadmodel->brush.RoundUpToHullSize = NULL;
7696         loadmodel->brush.PointInLeaf = Mod_OBJ_PointInLeaf;
7697         loadmodel->Draw = R_Q1BSP_Draw;
7698         loadmodel->DrawDepth = R_Q1BSP_DrawDepth;
7699         loadmodel->DrawDebug = R_Q1BSP_DrawDebug;
7700         loadmodel->DrawPrepass = R_Q1BSP_DrawPrepass;
7701         loadmodel->GetLightInfo = R_Q1BSP_GetLightInfo;
7702         loadmodel->CompileShadowMap = R_Q1BSP_CompileShadowMap;
7703         loadmodel->DrawShadowMap = R_Q1BSP_DrawShadowMap;
7704         loadmodel->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
7705         loadmodel->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
7706         loadmodel->DrawLight = R_Q1BSP_DrawLight;
7707
7708         VectorClear(mins);
7709         VectorClear(maxs);
7710
7711         // parse the OBJ text now
7712         for(;;)
7713         {
7714                 if (!*text)
7715                         break;
7716                 linenumber++;
7717                 linelen = 0;
7718                 for (linelen = 0;text[linelen] && text[linelen] != '\r' && text[linelen] != '\n';linelen++)
7719                         line[linelen] = text[linelen];
7720                 line[linelen] = 0;
7721                 for (argc = 0;argc < (int)(sizeof(argv)/sizeof(argv[0]));argc++)
7722                         argv[argc] = "";
7723                 argc = 0;
7724                 s = line;
7725                 while (*s == ' ' || *s == '\t')
7726                         s++;
7727                 while (*s)
7728                 {
7729                         argv[argc++] = s;
7730                         while (*s > ' ')
7731                                 s++;
7732                         if (!*s)
7733                                 break;
7734                         *s++ = 0;
7735                         while (*s == ' ' || *s == '\t')
7736                                 s++;
7737                 }
7738                 if (!argc)
7739                         continue;
7740                 if (argv[0][0] == '#')
7741                         continue;
7742                 if (!strcmp(argv[0], "v"))
7743                 {
7744                         if (maxv <= numv)
7745                         {
7746                                 float *oldv = v;
7747                                 maxv *= 2;
7748                                 v = Mem_Alloc(tempmempool, maxv * sizeof(float[3]));
7749                                 if (oldv)
7750                                 {
7751                                         memcpy(v, oldv, numv * sizeof(float[3]));
7752                                         Mem_Free(oldv);
7753                                 }
7754                         }
7755                         v[numv*3+0] = atof(argv[1]);
7756                         v[numv*3+1] = atof(argv[2]);
7757                         v[numv*3+2] = atof(argv[3]);
7758                         numv++;
7759                 }
7760                 else if (!strcmp(argv[0], "vt"))
7761                 {
7762                         if (maxvt <= numvt)
7763                         {
7764                                 float *oldvt = vt;
7765                                 maxvt *= 2;
7766                                 vt = Mem_Alloc(tempmempool, maxvt * sizeof(float[2]));
7767                                 if (oldvt)
7768                                 {
7769                                         memcpy(vt, oldvt, numvt * sizeof(float[2]));
7770                                         Mem_Free(oldvt);
7771                                 }
7772                         }
7773                         vt[numvt*2+0] = atof(argv[1]);
7774                         vt[numvt*2+1] = atof(argv[2]);
7775                         numvt++;
7776                 }
7777                 else if (!strcmp(argv[0], "vn"))
7778                 {
7779                         if (maxvn <= numvn)
7780                         {
7781                                 float *oldvn = vn;
7782                                 maxvn *= 2;
7783                                 vn = Mem_Alloc(tempmempool, maxvn * sizeof(float[3]));
7784                                 if (oldvn)
7785                                 {
7786                                         memcpy(vn, oldvn, numvn * sizeof(float[3]));
7787                                         Mem_Free(oldvn);
7788                                 }
7789                         }
7790                         vn[numvn*3+0] = atof(argv[1]);
7791                         vn[numvn*3+1] = atof(argv[2]);
7792                         vn[numvn*3+2] = atof(argv[3]);
7793                         numvn++;
7794                 }
7795                 else if (!strcmp(argv[0], "f"))
7796                 {
7797                         for (j = 1;j < argc;j++)
7798                         {
7799                                 index1 = atoi(argv[j]);
7800                                 while(argv[j][0] && argv[j][0] != '/')
7801                                         argv[j]++;
7802                                 if (argv[j][0])
7803                                         argv[j]++;
7804                                 index2 = atoi(argv[j]);
7805                                 while(argv[j][0] && argv[j][0] != '/')
7806                                         argv[j]++;
7807                                 if (argv[j][0])
7808                                         argv[j]++;
7809                                 index3 = atoi(argv[j]);
7810                                 // negative refers to a recent vertex
7811                                 // zero means not specified
7812                                 // positive means an absolute vertex index
7813                                 if (index1 < 0)
7814                                         index1 = numv - index1;
7815                                 if (index2 < 0)
7816                                         index2 = numvt - index2;
7817                                 if (index3 < 0)
7818                                         index3 = numvn - index3;
7819                                 VectorCopy(v + 3*index1, vcurrent.v);
7820                                 Vector2Copy(vt + 2*index2, vcurrent.vt);
7821                                 VectorCopy(vn + 3*index3, vcurrent.vn);
7822                                 if (numtriangles == 0)
7823                                 {
7824                                         VectorCopy(vcurrent.v, mins);
7825                                         VectorCopy(vcurrent.v, maxs);
7826                                 }
7827                                 else
7828                                 {
7829                                         mins[0] = min(mins[0], vcurrent.v[0]);
7830                                         mins[1] = min(mins[1], vcurrent.v[1]);
7831                                         mins[2] = min(mins[2], vcurrent.v[2]);
7832                                         maxs[0] = max(maxs[0], vcurrent.v[0]);
7833                                         maxs[1] = max(maxs[1], vcurrent.v[1]);
7834                                         maxs[2] = max(maxs[2], vcurrent.v[2]);
7835                                 }
7836                                 if (j == 1)
7837                                         vfirst = vcurrent;
7838                                 else if (j >= 3)
7839                                 {
7840                                         if (maxtriangles <= numtriangles)
7841                                         {
7842                                                 objtriangle_t *oldtriangles = triangles;
7843                                                 maxtriangles *= 2;
7844                                                 triangles = Mem_Alloc(tempmempool, maxtriangles * sizeof(*triangles));
7845                                                 if (oldtriangles)
7846                                                 {
7847                                                         memcpy(triangles, oldtriangles, maxtriangles * sizeof(*triangles));
7848                                                         Mem_Free(oldtriangles);
7849                                                 }
7850                                         }
7851                                         triangles[numtriangles].textureindex = textureindex;
7852                                         triangles[numtriangles].vertex[0] = vfirst;
7853                                         triangles[numtriangles].vertex[1] = vprev;
7854                                         triangles[numtriangles].vertex[2] = vcurrent;
7855                                         numtriangles++;
7856                                 }
7857                                 vprev = vcurrent;
7858                                 prev = index;
7859                         }
7860                 }
7861                 else if (!strcmp(argv[0], "o") || !strcmp(argv[0], "g"))
7862                         ;
7863                 else if (!!strcmp(argv[0], "usemtl"))
7864                 {
7865                         for (i = 0;i < numtextures;i++)
7866                                 if (!strcmp(texturenames[numtextures], argv[1]))
7867                                         break;
7868                         if (i < numtextures)
7869                                 texture = textures + i;
7870                         else
7871                         {
7872                                 if (maxtextures <= numtextures)
7873                                 {
7874                                         texture_t *oldtextures = textures;
7875                                         maxtextures *= 2;
7876                                         textures = Mem_Alloc(tempmempool, maxtextures * sizeof(*textures));
7877                                         if (oldtextures)
7878                                         {
7879                                                 memcpy(textures, oldtextures, numtextures * sizeof(*textures));
7880                                                 Mem_Free(oldtextures);
7881                                         }
7882                                 }
7883                                 textureindex = numtextures++;
7884                                 texturenames[textureindex] = Mem_Alloc(tempmempool, strlen(argv[1]) + 1);
7885                                 memcpy(texturenames[textureindex], argv[1], strlen(argv[1]) + 1);
7886                         }
7887                 }
7888                 text += linelen;
7889                 if (*text == '\r')
7890                         text++;
7891                 if (*text == '\n')
7892                         text++;
7893         }
7894
7895         // now that we have the OBJ data loaded as-is, we can convert it
7896
7897         // load the textures
7898         loadmodel->num_textures = numtextures;
7899         loadmodel->data_textures = Mem_Alloc(loadmodel->mempool, loadmodel->num_textures * sizeof(texture_t));
7900         for (i = 0;i < numtextures;i++)
7901                 Mod_LoadTextureFromQ3Shader(loadmodel->data_textures + i, texturenames[i], true, true, TEXF_MIPMAP | TEXF_ALPHA | (r_picmipworld.integer ? TEXF_PICMIP : 0) | TEXF_COMPRESS);
7902
7903         // free the texturenames array since we are now done with it
7904         for (i = 0;i < numtextures;i++)
7905         {
7906                 Mem_Free(texturenames[i]);
7907                 texturenames[i] = NULL;
7908         }
7909         Mem_Free(texturenames);
7910         texturenames = NULL;
7911
7912         // copy the model bounds, then enlarge the yaw and rotated bounds according to radius
7913         VectorCopy(mins, loadmodel->normalmins);
7914         VectorCopy(maxs, loadmodel->normalmaxs);
7915         dist = max(fabs(loadmodel->normalmins[0]), fabs(loadmodel->normalmaxs[0]));
7916         modelyawradius = max(fabs(loadmodel->normalmins[1]), fabs(loadmodel->normalmaxs[1]));
7917         modelyawradius = dist*dist+modelyawradius*modelyawradius;
7918         modelradius = max(fabs(loadmodel->normalmins[2]), fabs(loadmodel->normalmaxs[2]));
7919         modelradius = modelyawradius + modelradius * modelradius;
7920         modelyawradius = sqrt(modelyawradius);
7921         modelradius = sqrt(modelradius);
7922         loadmodel->yawmins[0] = loadmodel->yawmins[1] = -modelyawradius;
7923         loadmodel->yawmins[2] = loadmodel->normalmins[2];
7924         loadmodel->yawmaxs[0] = loadmodel->yawmaxs[1] =  modelyawradius;
7925         loadmodel->yawmaxs[2] = loadmodel->normalmaxs[2];
7926         loadmodel->rotatedmins[0] = loadmodel->rotatedmins[1] = loadmodel->rotatedmins[2] = -modelradius;
7927         loadmodel->rotatedmaxs[0] = loadmodel->rotatedmaxs[1] = loadmodel->rotatedmaxs[2] =  modelradius;
7928         loadmodel->radius = modelradius;
7929         loadmodel->radius2 = modelradius * modelradius;
7930
7931         // make sure the temp triangle buffer is big enough for BSP building
7932         maxclippedtriangles = numtriangles*4;
7933         if (numtriangles > 0)
7934         {
7935                 clippedfronttriangles = Mem_Alloc(loadmodel->mempool, maxclippedtriangles * 2 * sizeof(objtriangle_t));
7936                 clippedbacktriangles = clippedfronttriangles + maxclippedtriangles;
7937         }
7938
7939         // generate a rough BSP tree from triangle data, we don't have to be too careful here, it only has to define the basic areas of the map
7940         loadmodel->brush.num_leafs = 0;
7941         loadmodel->brush.num_nodes = 0;
7942         Mem_ExpandableArray_NewArray(&nodesarray, loadmodel->mempool, sizeof(objnode_t), 1024);
7943         rootnode = Mod_OBJ_BSPNodeForTriangles(triangles, numtriangles, mins, maxs, &nodesarray, maxclippedtriangles, clippedfronttriangles, clippedbacktriangles);
7944
7945         // convert the BSP tree to mnode_t and mleaf_t structures and convert the triangles to msurface_t...
7946         loadmodel->brush.data_leafs = Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_leafs * sizeof(mleaf_t));
7947         loadmodel->brush.data_nodes = Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_nodes * sizeof(mnode_t));
7948         loadmodel->brush.data_planes = Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_nodes * sizeof(mplane_t));
7949         loadmodel->brush.num_leafs = 0;
7950         loadmodel->brush.num_nodes = 0;
7951         loadmodel->brush.num_planes = 0;
7952         Mod_OBJ_ConvertAndFreeBSPNode(rootnode);
7953
7954         if (clippedfronttriangles)
7955                 Mem_Free(clippedfronttriangles);
7956         maxclippedtriangles = 0;
7957         clippedfronttriangles = NULL;
7958         clippedbacktriangles = NULL;
7959
7960 --- NOTHING DONE PAST THIS POINT ---
7961
7962         loadmodel->numskins = LittleLong(pinmodel->num_skins);
7963         numxyz = LittleLong(pinmodel->num_xyz);
7964         numst = LittleLong(pinmodel->num_st);
7965         loadmodel->surfmesh.num_triangles = LittleLong(pinmodel->num_tris);
7966         loadmodel->numframes = LittleLong(pinmodel->num_frames);
7967         loadmodel->surfmesh.num_morphframes = loadmodel->numframes;
7968         loadmodel->num_poses = loadmodel->surfmesh.num_morphframes;
7969         skinwidth = LittleLong(pinmodel->skinwidth);
7970         skinheight = LittleLong(pinmodel->skinheight);
7971         iskinwidth = 1.0f / skinwidth;
7972         iskinheight = 1.0f / skinheight;
7973
7974         loadmodel->num_surfaces = 1;
7975         loadmodel->nummodelsurfaces = loadmodel->num_surfaces;
7976         data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * sizeof(msurface_t) + loadmodel->num_surfaces * sizeof(int) + loadmodel->numframes * sizeof(animscene_t) + loadmodel->numframes * sizeof(float[6]) + loadmodel->surfmesh.num_triangles * sizeof(int[3]) + loadmodel->surfmesh.num_triangles * sizeof(int[3]));
7977         loadmodel->data_surfaces = (msurface_t *)data;data += loadmodel->num_surfaces * sizeof(msurface_t);
7978         loadmodel->sortedmodelsurfaces = (int *)data;data += loadmodel->num_surfaces * sizeof(int);
7979         loadmodel->sortedmodelsurfaces[0] = 0;
7980         loadmodel->animscenes = (animscene_t *)data;data += loadmodel->numframes * sizeof(animscene_t);
7981         loadmodel->surfmesh.data_morphmd2framesize6f = (float *)data;data += loadmodel->numframes * sizeof(float[6]);
7982         loadmodel->surfmesh.data_element3i = (int *)data;data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
7983         loadmodel->surfmesh.data_neighbor3i = (int *)data;data += loadmodel->surfmesh.num_triangles * sizeof(int[3]);
7984
7985         loadmodel->synctype = ST_RAND;
7986
7987         // load the skins
7988         inskin = (char *)(base + LittleLong(pinmodel->ofs_skins));
7989         skinfiles = Mod_LoadSkinFiles();
7990         if (skinfiles)
7991         {
7992                 loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
7993                 loadmodel->num_texturesperskin = loadmodel->num_surfaces;
7994                 loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
7995                 Mod_BuildAliasSkinsFromSkinFiles(loadmodel->data_textures, skinfiles, "default", "");
7996                 Mod_FreeSkinFiles(skinfiles);
7997         }
7998         else if (loadmodel->numskins)
7999         {
8000                 // skins found (most likely not a player model)
8001                 loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
8002                 loadmodel->num_texturesperskin = loadmodel->num_surfaces;
8003                 loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
8004                 for (i = 0;i < loadmodel->numskins;i++, inskin += MD2_SKINNAME)
8005                         Mod_LoadTextureFromQ3Shader(loadmodel->data_textures + i * loadmodel->num_surfaces, inskin, true, true, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PICMIP | TEXF_COMPRESS);
8006         }
8007         else
8008         {
8009                 // no skins (most likely a player model)
8010                 loadmodel->numskins = 1;
8011                 loadmodel->num_textures = loadmodel->num_surfaces * loadmodel->numskins;
8012                 loadmodel->num_texturesperskin = loadmodel->num_surfaces;
8013                 loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
8014                 Mod_BuildAliasSkinFromSkinFrame(loadmodel->data_textures, NULL);
8015         }
8016
8017         loadmodel->skinscenes = (animscene_t *)Mem_Alloc(loadmodel->mempool, sizeof(animscene_t) * loadmodel->numskins);
8018         for (i = 0;i < loadmodel->numskins;i++)
8019         {
8020                 loadmodel->skinscenes[i].firstframe = i;
8021                 loadmodel->skinscenes[i].framecount = 1;
8022                 loadmodel->skinscenes[i].loop = true;
8023                 loadmodel->skinscenes[i].framerate = 10;
8024         }
8025
8026         // load the triangles and stvert data
8027         inst = (unsigned short *)(base + LittleLong(pinmodel->ofs_st));
8028         intri = (md2triangle_t *)(base + LittleLong(pinmodel->ofs_tris));
8029         md2verthash = (struct md2verthash_s **)Mem_Alloc(tempmempool, 65536 * sizeof(hash));
8030         md2verthashdata = (struct md2verthash_s *)Mem_Alloc(tempmempool, loadmodel->surfmesh.num_triangles * 3 * sizeof(*hash));
8031         // swap the triangle list
8032         loadmodel->surfmesh.num_vertices = 0;
8033         for (i = 0;i < loadmodel->surfmesh.num_triangles;i++)
8034         {
8035                 for (j = 0;j < 3;j++)
8036                 {
8037                         xyz = (unsigned short) LittleShort (intri[i].index_xyz[j]);
8038                         st = (unsigned short) LittleShort (intri[i].index_st[j]);
8039                         if (xyz >= numxyz)
8040                         {
8041                                 Con_Printf("%s has an invalid xyz index (%i) on triangle %i, resetting to 0\n", loadmodel->name, xyz, i);
8042                                 xyz = 0;
8043                         }
8044                         if (st >= numst)
8045                         {
8046                                 Con_Printf("%s has an invalid st index (%i) on triangle %i, resetting to 0\n", loadmodel->name, st, i);
8047                                 st = 0;
8048                         }
8049                         hashindex = (xyz * 256 + st) & 65535;
8050                         for (hash = md2verthash[hashindex];hash;hash = hash->next)
8051                                 if (hash->xyz == xyz && hash->st == st)
8052                                         break;
8053                         if (hash == NULL)
8054                         {
8055                                 hash = md2verthashdata + loadmodel->surfmesh.num_vertices++;
8056                                 hash->xyz = xyz;
8057                                 hash->st = st;
8058                                 hash->next = md2verthash[hashindex];
8059                                 md2verthash[hashindex] = hash;
8060                         }
8061                         loadmodel->surfmesh.data_element3i[i*3+j] = (hash - md2verthashdata);
8062                 }
8063         }
8064
8065         vertremap = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * sizeof(int));
8066         data = (unsigned char *)Mem_Alloc(loadmodel->mempool, loadmodel->surfmesh.num_vertices * sizeof(float[2]) + loadmodel->surfmesh.num_vertices * loadmodel->surfmesh.num_morphframes * sizeof(trivertx_t));
8067         loadmodel->surfmesh.data_texcoordtexture2f = (float *)data;data += loadmodel->surfmesh.num_vertices * sizeof(float[2]);
8068         loadmodel->surfmesh.data_morphmdlvertex = (trivertx_t *)data;data += loadmodel->surfmesh.num_vertices * loadmodel->surfmesh.num_morphframes * sizeof(trivertx_t);
8069         for (i = 0;i < loadmodel->surfmesh.num_vertices;i++)
8070         {
8071                 int sts, stt;
8072                 hash = md2verthashdata + i;
8073                 vertremap[i] = hash->xyz;
8074                 sts = LittleShort(inst[hash->st*2+0]);
8075                 stt = LittleShort(inst[hash->st*2+1]);
8076                 if (sts < 0 || sts >= skinwidth || stt < 0 || stt >= skinheight)
8077                 {
8078                         Con_Printf("%s has an invalid skin coordinate (%i %i) on vert %i, changing to 0 0\n", loadmodel->name, sts, stt, i);
8079                         sts = 0;
8080                         stt = 0;
8081                 }
8082                 loadmodel->surfmesh.data_texcoordtexture2f[i*2+0] = sts * iskinwidth;
8083                 loadmodel->surfmesh.data_texcoordtexture2f[i*2+1] = stt * iskinheight;
8084         }
8085
8086         Mem_Free(md2verthash);
8087         Mem_Free(md2verthashdata);
8088
8089         // generate ushort elements array if possible
8090         if (loadmodel->surfmesh.num_vertices <= 65536)
8091                 loadmodel->surfmesh.data_element3s = (unsigned short *)Mem_Alloc(loadmodel->mempool, sizeof(unsigned short[3]) * loadmodel->surfmesh.num_triangles);
8092
8093         // load the frames
8094         datapointer = (base + LittleLong(pinmodel->ofs_frames));
8095         for (i = 0;i < loadmodel->surfmesh.num_morphframes;i++)
8096         {
8097                 int k;
8098                 trivertx_t *v;
8099                 trivertx_t *out;
8100                 pinframe = (md2frame_t *)datapointer;
8101                 datapointer += sizeof(md2frame_t);
8102                 // store the frame scale/translate into the appropriate array
8103                 for (j = 0;j < 3;j++)
8104                 {
8105                         loadmodel->surfmesh.data_morphmd2framesize6f[i*6+j] = LittleFloat(pinframe->scale[j]);
8106                         loadmodel->surfmesh.data_morphmd2framesize6f[i*6+3+j] = LittleFloat(pinframe->translate[j]);
8107                 }
8108                 // convert the vertices
8109                 v = (trivertx_t *)datapointer;
8110                 out = loadmodel->surfmesh.data_morphmdlvertex + i * loadmodel->surfmesh.num_vertices;
8111                 for (k = 0;k < loadmodel->surfmesh.num_vertices;k++)
8112                         out[k] = v[vertremap[k]];
8113                 datapointer += numxyz * sizeof(trivertx_t);
8114
8115                 strlcpy(loadmodel->animscenes[i].name, pinframe->name, sizeof(loadmodel->animscenes[i].name));
8116                 loadmodel->animscenes[i].firstframe = i;
8117                 loadmodel->animscenes[i].framecount = 1;
8118                 loadmodel->animscenes[i].framerate = 10;
8119                 loadmodel->animscenes[i].loop = true;
8120         }
8121
8122         Mem_Free(vertremap);
8123
8124         Mod_MakeSortedSurfaces(loadmodel);
8125         Mod_BuildTriangleNeighbors(loadmodel->surfmesh.data_neighbor3i, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.num_triangles);
8126         Mod_Alias_CalculateBoundingBox();
8127         Mod_Alias_MorphMesh_CompileFrames();
8128
8129         surface = loadmodel->data_surfaces;
8130         surface->texture = loadmodel->data_textures;
8131         surface->num_firsttriangle = 0;
8132         surface->num_triangles = loadmodel->surfmesh.num_triangles;
8133         surface->num_firstvertex = 0;
8134         surface->num_vertices = loadmodel->surfmesh.num_vertices;
8135
8136         loadmodel->surfmesh.isanimated = false;
8137
8138         if (loadmodel->surfmesh.data_element3s)
8139                 for (i = 0;i < loadmodel->surfmesh.num_triangles*3;i++)
8140                         loadmodel->surfmesh.data_element3s[i] = loadmodel->surfmesh.data_element3i[i];
8141 #endif
8142 }
8143 #endif // !OBJASMODEL
8144
8145 qboolean Mod_CanSeeBox_Trace(int numsamples, float t, dp_model_t *model, vec3_t eye, vec3_t minsX, vec3_t maxsX)
8146 {
8147         // we already have done PVS culling at this point...
8148         // so we don't need to do it again.
8149
8150         int i;
8151         vec3_t testorigin, mins, maxs;
8152
8153         testorigin[0] = (minsX[0] + maxsX[0]) * 0.5;
8154         testorigin[1] = (minsX[1] + maxsX[1]) * 0.5;
8155         testorigin[2] = (minsX[2] + maxsX[2]) * 0.5;
8156
8157         if(model->brush.TraceLineOfSight(model, eye, testorigin))
8158                 return 1;
8159
8160         // expand the box a little
8161         mins[0] = (t+1) * minsX[0] - t * maxsX[0];
8162         maxs[0] = (t+1) * maxsX[0] - t * minsX[0];
8163         mins[1] = (t+1) * minsX[1] - t * maxsX[1];
8164         maxs[1] = (t+1) * maxsX[1] - t * minsX[1];
8165         mins[2] = (t+1) * minsX[2] - t * maxsX[2];
8166         maxs[2] = (t+1) * maxsX[2] - t * minsX[2];
8167
8168         for(i = 0; i != numsamples; ++i)
8169         {
8170                 testorigin[0] = lhrandom(mins[0], maxs[0]);
8171                 testorigin[1] = lhrandom(mins[1], maxs[1]);
8172                 testorigin[2] = lhrandom(mins[2], maxs[2]);
8173
8174                 if(model->brush.TraceLineOfSight(model, eye, testorigin))
8175                         return 1;
8176         }
8177
8178         return 0;
8179 }
8180