]> icculus.org git repositories - divverent/darkplaces.git/blob - model_brush.c
reverted RunParticleEffect appearance in non-GOODVSBAD2 mode because they were far...
[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
25 // note: model_shared.c sets up r_notexture, and r_surf_notexture
26
27 qbyte mod_q1bsp_novis[(MAX_MAP_LEAFS + 7)/ 8];
28
29 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
30 cvar_t halflifebsp = {0, "halflifebsp", "0"};
31 cvar_t r_novis = {0, "r_novis", "0"};
32 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
33 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
34 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
35 cvar_t r_sortsurfaces = {0, "r_sortsurfaces", "0"};
36
37 void Mod_BrushInit(void)
38 {
39 //      Cvar_RegisterVariable(&r_subdivide_size);
40         Cvar_RegisterVariable(&halflifebsp);
41         Cvar_RegisterVariable(&r_novis);
42         Cvar_RegisterVariable(&r_miplightmaps);
43         Cvar_RegisterVariable(&r_lightmaprgba);
44         Cvar_RegisterVariable(&r_nosurftextures);
45         Cvar_RegisterVariable(&r_sortsurfaces);
46         memset(mod_q1bsp_novis, 0xff, sizeof(mod_q1bsp_novis));
47 }
48
49 static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
50 {
51         mnode_t *node;
52
53         if (model == NULL)
54                 return NULL;
55
56         Mod_CheckLoaded(model);
57
58         // LordHavoc: modified to start at first clip node,
59         // in other words: first node of the (sub)model
60         node = model->nodes + model->hulls[0].firstclipnode;
61         while (node->contents == 0)
62                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
63
64         return (mleaf_t *)node;
65 }
66
67 static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p)
68 {
69         mnode_t *node;
70
71         if (model == NULL)
72                 return CONTENTS_EMPTY;
73
74         Mod_CheckLoaded(model);
75
76         // LordHavoc: modified to start at first clip node,
77         // in other words: first node of the (sub)model
78         node = model->nodes + model->hulls[0].firstclipnode;
79         while (node->contents == 0)
80                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
81
82         return ((mleaf_t *)node)->contents;
83 }
84
85 typedef struct findnonsolidlocationinfo_s
86 {
87         vec3_t center;
88         vec_t radius;
89         vec3_t nudge;
90         vec_t bestdist;
91         model_t *model;
92 }
93 findnonsolidlocationinfo_t;
94
95 #if 0
96 extern cvar_t samelevel;
97 #endif
98 static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
99 {
100         int i, surfnum, k, *tri, *mark;
101         float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
102 #if 0
103         float surfnormal[3];
104 #endif
105         msurface_t *surf;
106         surfmesh_t *mesh;
107         for (surfnum = 0, mark = leaf->firstmarksurface;surfnum < leaf->nummarksurfaces;surfnum++, mark++)
108         {
109                 surf = info->model->surfaces + *mark;
110                 if (surf->flags & SURF_SOLIDCLIP)
111                 {
112 #if 0
113                         VectorCopy(surf->plane->normal, surfnormal);
114                         if (surf->flags & SURF_PLANEBACK)
115                                 VectorNegate(surfnormal, surfnormal);
116 #endif
117                         for (mesh = surf->mesh;mesh;mesh = mesh->chain)
118                         {
119                                 for (k = 0;k < mesh->numtriangles;k++)
120                                 {
121                                         tri = mesh->element3i + k * 3;
122                                         VectorCopy((mesh->vertex3f + tri[0] * 3), vert[0]);
123                                         VectorCopy((mesh->vertex3f + tri[1] * 3), vert[1]);
124                                         VectorCopy((mesh->vertex3f + tri[2] * 3), vert[2]);
125                                         VectorSubtract(vert[1], vert[0], edge[0]);
126                                         VectorSubtract(vert[2], vert[1], edge[1]);
127                                         CrossProduct(edge[1], edge[0], facenormal);
128                                         if (facenormal[0] || facenormal[1] || facenormal[2])
129                                         {
130                                                 VectorNormalize(facenormal);
131 #if 0
132                                                 if (VectorDistance(facenormal, surfnormal) > 0.01f)
133                                                         Con_Printf("a2! %f %f %f != %f %f %f\n", facenormal[0], facenormal[1], facenormal[2], surfnormal[0], surfnormal[1], surfnormal[2]);
134 #endif
135                                                 f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
136                                                 if (f <= info->bestdist && f >= -info->bestdist)
137                                                 {
138                                                         VectorSubtract(vert[0], vert[2], edge[2]);
139                                                         VectorNormalize(edge[0]);
140                                                         VectorNormalize(edge[1]);
141                                                         VectorNormalize(edge[2]);
142                                                         CrossProduct(facenormal, edge[0], edgenormal[0]);
143                                                         CrossProduct(facenormal, edge[1], edgenormal[1]);
144                                                         CrossProduct(facenormal, edge[2], edgenormal[2]);
145 #if 0
146                                                         if (samelevel.integer & 1)
147                                                                 VectorNegate(edgenormal[0], edgenormal[0]);
148                                                         if (samelevel.integer & 2)
149                                                                 VectorNegate(edgenormal[1], edgenormal[1]);
150                                                         if (samelevel.integer & 4)
151                                                                 VectorNegate(edgenormal[2], edgenormal[2]);
152                                                         for (i = 0;i < 3;i++)
153                                                                 if (DotProduct(vert[0], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
154                                                                  || DotProduct(vert[1], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
155                                                                  || DotProduct(vert[2], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f)
156                                                                         Con_Printf("a! %i : %f %f %f (%f %f %f)\n", i, edgenormal[i][0], edgenormal[i][1], edgenormal[i][2], facenormal[0], facenormal[1], facenormal[2]);
157 #endif
158                                                         // face distance
159                                                         if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
160                                                          && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
161                                                          && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
162                                                         {
163                                                                 // we got lucky, the center is within the face
164                                                                 dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
165                                                                 if (dist < 0)
166                                                                 {
167                                                                         dist = -dist;
168                                                                         if (info->bestdist > dist)
169                                                                         {
170                                                                                 info->bestdist = dist;
171                                                                                 VectorScale(facenormal, (info->radius - -dist), info->nudge);
172                                                                         }
173                                                                 }
174                                                                 else
175                                                                 {
176                                                                         if (info->bestdist > dist)
177                                                                         {
178                                                                                 info->bestdist = dist;
179                                                                                 VectorScale(facenormal, (info->radius - dist), info->nudge);
180                                                                         }
181                                                                 }
182                                                         }
183                                                         else
184                                                         {
185                                                                 // check which edge or vertex the center is nearest
186                                                                 for (i = 0;i < 3;i++)
187                                                                 {
188                                                                         f = DotProduct(info->center, edge[i]);
189                                                                         if (f >= DotProduct(vert[0], edge[i])
190                                                                          && f <= DotProduct(vert[1], edge[i]))
191                                                                         {
192                                                                                 // on edge
193                                                                                 VectorMA(info->center, -f, edge[i], point);
194                                                                                 dist = sqrt(DotProduct(point, point));
195                                                                                 if (info->bestdist > dist)
196                                                                                 {
197                                                                                         info->bestdist = dist;
198                                                                                         VectorScale(point, (info->radius / dist), info->nudge);
199                                                                                 }
200                                                                                 // skip both vertex checks
201                                                                                 // (both are further away than this edge)
202                                                                                 i++;
203                                                                         }
204                                                                         else
205                                                                         {
206                                                                                 // not on edge, check first vertex of edge
207                                                                                 VectorSubtract(info->center, vert[i], point);
208                                                                                 dist = sqrt(DotProduct(point, point));
209                                                                                 if (info->bestdist > dist)
210                                                                                 {
211                                                                                         info->bestdist = dist;
212                                                                                         VectorScale(point, (info->radius / dist), info->nudge);
213                                                                                 }
214                                                                         }
215                                                                 }
216                                                         }
217                                                 }
218                                         }
219                                 }
220                         }
221                 }
222         }
223 }
224
225 static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
226 {
227         if (node->contents)
228         {
229                 if (((mleaf_t *)node)->nummarksurfaces)
230                         Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
231         }
232         else
233         {
234                 float f = PlaneDiff(info->center, node->plane);
235                 if (f >= -info->bestdist)
236                         Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[0]);
237                 if (f <= info->bestdist)
238                         Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[1]);
239         }
240 }
241
242 static void Mod_Q1BSP_FindNonSolidLocation(model_t *model, vec3_t in, vec3_t out, float radius)
243 {
244         int i;
245         findnonsolidlocationinfo_t info;
246         if (model == NULL)
247         {
248                 VectorCopy(in, out);
249                 return;
250         }
251         VectorCopy(in, info.center);
252         info.radius = radius;
253         info.model = model;
254         i = 0;
255         do
256         {
257                 VectorClear(info.nudge);
258                 info.bestdist = radius;
259                 Mod_Q1BSP_FindNonSolidLocation_r(&info, model->nodes + model->hulls[0].firstclipnode);
260                 VectorAdd(info.center, info.nudge, info.center);
261         }
262         while (info.bestdist < radius && ++i < 10);
263         VectorCopy(info.center, out);
264 }
265
266 static qbyte *Mod_Q1BSP_DecompressVis(model_t *model, qbyte *in)
267 {
268         static qbyte decompressed[MAX_MAP_LEAFS/8];
269         int c;
270         qbyte *out;
271         int row;
272
273         row = (model->numleafs+7)>>3;
274         out = decompressed;
275
276         do
277         {
278                 if (*in)
279                 {
280                         *out++ = *in++;
281                         continue;
282                 }
283
284                 c = in[1];
285                 in += 2;
286                 while (c)
287                 {
288                         *out++ = 0;
289                         c--;
290                 }
291         } while (out - decompressed < row);
292
293         return decompressed;
294 }
295
296 static qbyte *Mod_Q1BSP_LeafPVS(model_t *model, mleaf_t *leaf)
297 {
298         if (r_novis.integer || leaf == model->leafs || leaf->compressed_vis == NULL)
299                 return mod_q1bsp_novis;
300         return Mod_Q1BSP_DecompressVis(model, leaf->compressed_vis);
301 }
302
303 static void Mod_Q1BSP_LoadTextures(lump_t *l)
304 {
305         int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
306         miptex_t *dmiptex;
307         texture_t *tx, *tx2, *anims[10], *altanims[10];
308         dmiptexlump_t *m;
309         qbyte *data, *mtdata;
310         char name[256];
311
312         loadmodel->textures = NULL;
313
314         if (!l->filelen)
315                 return;
316
317         m = (dmiptexlump_t *)(mod_base + l->fileofs);
318
319         m->nummiptex = LittleLong (m->nummiptex);
320
321         // add two slots for notexture walls and notexture liquids
322         loadmodel->numtextures = m->nummiptex + 2;
323         loadmodel->textures = Mem_Alloc(loadmodel->mempool, loadmodel->numtextures * sizeof(texture_t));
324
325         // fill out all slots with notexture
326         for (i = 0, tx = loadmodel->textures;i < loadmodel->numtextures;i++, tx++)
327         {
328                 tx->number = i;
329                 strcpy(tx->name, "NO TEXTURE FOUND");
330                 tx->width = 16;
331                 tx->height = 16;
332                 tx->skin.base = r_notexture;
333                 tx->shader = &Cshader_wall_lightmap;
334                 tx->flags = SURF_SOLIDCLIP;
335                 if (i == loadmodel->numtextures - 1)
336                 {
337                         tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
338                         tx->shader = &Cshader_water;
339                 }
340                 tx->currentframe = tx;
341         }
342
343         // just to work around bounds checking when debugging with it (array index out of bounds error thing)
344         dofs = m->dataofs;
345         // LordHavoc: mostly rewritten map texture loader
346         for (i = 0;i < m->nummiptex;i++)
347         {
348                 dofs[i] = LittleLong(dofs[i]);
349                 if (dofs[i] == -1 || r_nosurftextures.integer)
350                         continue;
351                 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
352
353                 // make sure name is no more than 15 characters
354                 for (j = 0;dmiptex->name[j] && j < 15;j++)
355                         name[j] = dmiptex->name[j];
356                 name[j] = 0;
357
358                 mtwidth = LittleLong(dmiptex->width);
359                 mtheight = LittleLong(dmiptex->height);
360                 mtdata = NULL;
361                 j = LittleLong(dmiptex->offsets[0]);
362                 if (j)
363                 {
364                         // texture included
365                         if (j < 40 || j + mtwidth * mtheight > l->filelen)
366                         {
367                                 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
368                                 continue;
369                         }
370                         mtdata = (qbyte *)dmiptex + j;
371                 }
372
373                 if ((mtwidth & 15) || (mtheight & 15))
374                         Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
375
376                 // LordHavoc: force all names to lowercase
377                 for (j = 0;name[j];j++)
378                         if (name[j] >= 'A' && name[j] <= 'Z')
379                                 name[j] += 'a' - 'A';
380
381                 tx = loadmodel->textures + i;
382                 strcpy(tx->name, name);
383                 tx->width = mtwidth;
384                 tx->height = mtheight;
385
386                 if (!tx->name[0])
387                 {
388                         sprintf(tx->name, "unnamed%i", i);
389                         Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
390                 }
391
392                 // LordHavoc: HL sky textures are entirely different than quake
393                 if (!loadmodel->ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
394                 {
395                         if (loadmodel->isworldmodel)
396                         {
397                                 data = loadimagepixels(tx->name, false, 0, 0);
398                                 if (data)
399                                 {
400                                         if (image_width == 256 && image_height == 128)
401                                         {
402                                                 R_InitSky(data, 4);
403                                                 Mem_Free(data);
404                                         }
405                                         else
406                                         {
407                                                 Mem_Free(data);
408                                                 Con_Printf("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
409                                                 if (mtdata != NULL)
410                                                         R_InitSky(mtdata, 1);
411                                         }
412                                 }
413                                 else if (mtdata != NULL)
414                                         R_InitSky(mtdata, 1);
415                         }
416                 }
417                 else
418                 {
419                         if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true))
420                         {
421                                 // did not find external texture, load it from the bsp or wad3
422                                 if (loadmodel->ishlbsp)
423                                 {
424                                         // internal texture overrides wad
425                                         qbyte *pixels, *freepixels, *fogpixels;
426                                         pixels = freepixels = NULL;
427                                         if (mtdata)
428                                                 pixels = W_ConvertWAD3Texture(dmiptex);
429                                         if (pixels == NULL)
430                                                 pixels = freepixels = W_GetTexture(tx->name);
431                                         if (pixels != NULL)
432                                         {
433                                                 tx->width = image_width;
434                                                 tx->height = image_height;
435                                                 tx->skin.base = tx->skin.merged = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
436                                                 if (Image_CheckAlpha(pixels, image_width * image_height, true))
437                                                 {
438                                                         fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4);
439                                                         for (j = 0;j < image_width * image_height * 4;j += 4)
440                                                         {
441                                                                 fogpixels[j + 0] = 255;
442                                                                 fogpixels[j + 1] = 255;
443                                                                 fogpixels[j + 2] = 255;
444                                                                 fogpixels[j + 3] = pixels[j + 3];
445                                                         }
446                                                         tx->skin.fog = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
447                                                         Mem_Free(fogpixels);
448                                                 }
449                                         }
450                                         if (freepixels)
451                                                 Mem_Free(freepixels);
452                                 }
453                                 else if (mtdata) // texture included
454                                         Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE, false, true, tx->name[0] != '*' && r_fullbrights.integer, mtdata, tx->width, tx->height);
455                         }
456                 }
457                 if (tx->skin.base == NULL)
458                 {
459                         // no texture found
460                         tx->width = 16;
461                         tx->height = 16;
462                         tx->skin.base = r_notexture;
463                 }
464
465                 if (tx->name[0] == '*')
466                 {
467                         // turb does not block movement
468                         tx->flags &= ~SURF_SOLIDCLIP;
469                         tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
470                         // LordHavoc: some turbulent textures should be fullbright and solid
471                         if (!strncmp(tx->name,"*lava",5)
472                          || !strncmp(tx->name,"*teleport",9)
473                          || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
474                                 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
475                         else
476                                 tx->flags |= SURF_WATERALPHA;
477                         tx->shader = &Cshader_water;
478                 }
479                 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
480                 {
481                         tx->flags |= SURF_DRAWSKY;
482                         tx->shader = &Cshader_sky;
483                 }
484                 else
485                 {
486                         tx->flags |= SURF_LIGHTMAP;
487                         if (!tx->skin.fog)
488                                 tx->flags |= SURF_SHADOWCAST | SURF_SHADOWLIGHT;
489                         tx->shader = &Cshader_wall_lightmap;
490                 }
491
492                 // start out with no animation
493                 tx->currentframe = tx;
494         }
495
496         // sequence the animations
497         for (i = 0;i < m->nummiptex;i++)
498         {
499                 tx = loadmodel->textures + i;
500                 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
501                         continue;
502                 if (tx->anim_total[0] || tx->anim_total[1])
503                         continue;       // already sequenced
504
505                 // find the number of frames in the animation
506                 memset(anims, 0, sizeof(anims));
507                 memset(altanims, 0, sizeof(altanims));
508
509                 for (j = i;j < m->nummiptex;j++)
510                 {
511                         tx2 = loadmodel->textures + j;
512                         if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
513                                 continue;
514
515                         num = tx2->name[1];
516                         if (num >= '0' && num <= '9')
517                                 anims[num - '0'] = tx2;
518                         else if (num >= 'a' && num <= 'j')
519                                 altanims[num - 'a'] = tx2;
520                         else
521                                 Con_Printf("Bad animating texture %s\n", tx->name);
522                 }
523
524                 max = altmax = 0;
525                 for (j = 0;j < 10;j++)
526                 {
527                         if (anims[j])
528                                 max = j + 1;
529                         if (altanims[j])
530                                 altmax = j + 1;
531                 }
532                 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
533
534                 incomplete = false;
535                 for (j = 0;j < max;j++)
536                 {
537                         if (!anims[j])
538                         {
539                                 Con_Printf("Missing frame %i of %s\n", j, tx->name);
540                                 incomplete = true;
541                         }
542                 }
543                 for (j = 0;j < altmax;j++)
544                 {
545                         if (!altanims[j])
546                         {
547                                 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
548                                 incomplete = true;
549                         }
550                 }
551                 if (incomplete)
552                         continue;
553
554                 if (altmax < 1)
555                 {
556                         // if there is no alternate animation, duplicate the primary
557                         // animation into the alternate
558                         altmax = max;
559                         for (k = 0;k < 10;k++)
560                                 altanims[k] = anims[k];
561                 }
562
563                 // link together the primary animation
564                 for (j = 0;j < max;j++)
565                 {
566                         tx2 = anims[j];
567                         tx2->animated = true;
568                         tx2->anim_total[0] = max;
569                         tx2->anim_total[1] = altmax;
570                         for (k = 0;k < 10;k++)
571                         {
572                                 tx2->anim_frames[0][k] = anims[k];
573                                 tx2->anim_frames[1][k] = altanims[k];
574                         }
575                 }
576
577                 // if there really is an alternate anim...
578                 if (anims[0] != altanims[0])
579                 {
580                         // link together the alternate animation
581                         for (j = 0;j < altmax;j++)
582                         {
583                                 tx2 = altanims[j];
584                                 tx2->animated = true;
585                                 // the primary/alternate are reversed here
586                                 tx2->anim_total[0] = altmax;
587                                 tx2->anim_total[1] = max;
588                                 for (k = 0;k < 10;k++)
589                                 {
590                                         tx2->anim_frames[0][k] = altanims[k];
591                                         tx2->anim_frames[1][k] = anims[k];
592                                 }
593                         }
594                 }
595         }
596 }
597
598 static void Mod_Q1BSP_LoadLighting(lump_t *l)
599 {
600         int i;
601         qbyte *in, *out, *data, d;
602         char litfilename[1024];
603         loadmodel->lightdata = NULL;
604         if (loadmodel->ishlbsp) // LordHavoc: load the colored lighting data straight
605         {
606                 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
607                 memcpy(loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
608         }
609         else // LordHavoc: bsp version 29 (normal white lighting)
610         {
611                 // LordHavoc: hope is not lost yet, check for a .lit file to load
612                 strcpy(litfilename, loadmodel->name);
613                 FS_StripExtension(litfilename, litfilename);
614                 strcat(litfilename, ".lit");
615                 data = (qbyte*) FS_LoadFile(litfilename, false);
616                 if (data)
617                 {
618                         if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
619                         {
620                                 i = LittleLong(((int *)data)[1]);
621                                 if (i == 1)
622                                 {
623                                         Con_DPrintf("loaded %s\n", litfilename);
624                                         loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
625                                         memcpy(loadmodel->lightdata, data + 8, fs_filesize - 8);
626                                         Mem_Free(data);
627                                         return;
628                                 }
629                                 else
630                                 {
631                                         Con_Printf("Unknown .lit file version (%d)\n", i);
632                                         Mem_Free(data);
633                                 }
634                         }
635                         else
636                         {
637                                 if (fs_filesize == 8)
638                                         Con_Printf("Empty .lit file, ignoring\n");
639                                 else
640                                         Con_Printf("Corrupt .lit file (old version?), ignoring\n");
641                                 Mem_Free(data);
642                         }
643                 }
644                 // LordHavoc: oh well, expand the white lighting data
645                 if (!l->filelen)
646                         return;
647                 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
648                 in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
649                 out = loadmodel->lightdata;
650                 memcpy(in, mod_base + l->fileofs, l->filelen);
651                 for (i = 0;i < l->filelen;i++)
652                 {
653                         d = *in++;
654                         *out++ = d;
655                         *out++ = d;
656                         *out++ = d;
657                 }
658         }
659 }
660
661 static void Mod_Q1BSP_LoadLightList(void)
662 {
663         int a, n, numlights;
664         char lightsfilename[1024], *s, *t, *lightsstring;
665         mlight_t *e;
666
667         strcpy(lightsfilename, loadmodel->name);
668         FS_StripExtension(lightsfilename, lightsfilename);
669         strcat(lightsfilename, ".lights");
670         s = lightsstring = (char *) FS_LoadFile(lightsfilename, false);
671         if (s)
672         {
673                 numlights = 0;
674                 while (*s)
675                 {
676                         while (*s && *s != '\n')
677                                 s++;
678                         if (!*s)
679                         {
680                                 Mem_Free(lightsstring);
681                                 Host_Error("lights file must end with a newline\n");
682                         }
683                         s++;
684                         numlights++;
685                 }
686                 loadmodel->lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
687                 s = lightsstring;
688                 n = 0;
689                 while (*s && n < numlights)
690                 {
691                         t = s;
692                         while (*s && *s != '\n')
693                                 s++;
694                         if (!*s)
695                         {
696                                 Mem_Free(lightsstring);
697                                 Host_Error("misparsed lights file!\n");
698                         }
699                         e = loadmodel->lights + n;
700                         *s = 0;
701                         a = sscanf(t, "%f %f %f %f %f %f %f %f %f %f %f %f %f %d", &e->origin[0], &e->origin[1], &e->origin[2], &e->falloff, &e->light[0], &e->light[1], &e->light[2], &e->subtract, &e->spotdir[0], &e->spotdir[1], &e->spotdir[2], &e->spotcone, &e->distbias, &e->style);
702                         *s = '\n';
703                         if (a != 14)
704                         {
705                                 Mem_Free(lightsstring);
706                                 Host_Error("invalid lights file, found %d parameters on line %i, should be 14 parameters (origin[0] origin[1] origin[2] falloff light[0] light[1] light[2] subtract spotdir[0] spotdir[1] spotdir[2] spotcone distancebias style)\n", a, n + 1);
707                         }
708                         s++;
709                         n++;
710                 }
711                 if (*s)
712                 {
713                         Mem_Free(lightsstring);
714                         Host_Error("misparsed lights file!\n");
715                 }
716                 loadmodel->numlights = numlights;
717                 Mem_Free(lightsstring);
718         }
719 }
720
721 /*
722 static int castshadowcount = 0;
723 static void Mod_Q1BSP_ProcessLightList(void)
724 {
725         int j, k, l, *mark, lnum;
726         mlight_t *e;
727         msurface_t *surf;
728         float dist;
729         mleaf_t *leaf;
730         qbyte *pvs;
731         vec3_t temp;
732         float *v, radius2;
733         for (lnum = 0, e = loadmodel->lights;lnum < loadmodel->numlights;lnum++, e++)
734         {
735                 e->cullradius2 = DotProduct(e->light, e->light) / (e->falloff * e->falloff * 8192.0f * 8192.0f * 2.0f * 2.0f);// + 4096.0f;
736                 if (e->cullradius2 > 4096.0f * 4096.0f)
737                         e->cullradius2 = 4096.0f * 4096.0f;
738                 e->cullradius = e->lightradius = sqrt(e->cullradius2);
739                 leaf = Mod_Q1BSP_PointInLeaf(e->origin, loadmodel);
740                 if (leaf->compressed_vis)
741                         pvs = Mod_Q1BSP_DecompressVis(leaf->compressed_vis, loadmodel);
742                 else
743                         pvs = mod_q1bsp_novis;
744                 for (j = 0;j < loadmodel->numsurfaces;j++)
745                         loadmodel->surfacevisframes[j] = -1;
746                 for (j = 0, leaf = loadmodel->leafs + 1;j < loadmodel->numleafs - 1;j++, leaf++)
747                 {
748                         if (pvs[j >> 3] & (1 << (j & 7)))
749                         {
750                                 for (k = 0, mark = leaf->firstmarksurface;k < leaf->nummarksurfaces;k++, mark++)
751                                 {
752                                         surf = loadmodel->surfaces + *mark;
753                                         if (surf->number != *mark)
754                                                 Con_Printf("%d != %d\n", surf->number, *mark);
755                                         dist = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
756                                         if (surf->flags & SURF_PLANEBACK)
757                                                 dist = -dist;
758                                         if (dist > 0 && dist < e->cullradius)
759                                         {
760                                                 temp[0] = bound(surf->poly_mins[0], e->origin[0], surf->poly_maxs[0]) - e->origin[0];
761                                                 temp[1] = bound(surf->poly_mins[1], e->origin[1], surf->poly_maxs[1]) - e->origin[1];
762                                                 temp[2] = bound(surf->poly_mins[2], e->origin[2], surf->poly_maxs[2]) - e->origin[2];
763                                                 if (DotProduct(temp, temp) < lightradius2)
764                                                         loadmodel->surfacevisframes[*mark] = -2;
765                                         }
766                                 }
767                         }
768                 }
769                 // build list of light receiving surfaces
770                 e->numsurfaces = 0;
771                 for (j = 0;j < loadmodel->numsurfaces;j++)
772                         if (loadmodel->surfacevisframes[j] == -2)
773                                 e->numsurfaces++;
774                 e->surfaces = NULL;
775                 if (e->numsurfaces > 0)
776                 {
777                         e->surfaces = Mem_Alloc(loadmodel->mempool, sizeof(msurface_t *) * e->numsurfaces);
778                         e->numsurfaces = 0;
779                         for (j = 0;j < loadmodel->numsurfaces;j++)
780                                 if (loadmodel->surfacevisframes[j] == -2)
781                                         e->surfaces[e->numsurfaces++] = loadmodel->surfaces + j;
782                 }
783                 // find bounding box and sphere of lit surfaces
784                 // (these will be used for creating a shape to clip the light)
785                 radius2 = 0;
786                 for (j = 0;j < e->numsurfaces;j++)
787                 {
788                         surf = e->surfaces[j];
789                         if (j == 0)
790                         {
791                                 VectorCopy(surf->poly_verts, e->mins);
792                                 VectorCopy(surf->poly_verts, e->maxs);
793                         }
794                         for (k = 0, v = surf->poly_verts;k < surf->poly_numverts;k++, v += 3)
795                         {
796                                 if (e->mins[0] > v[0]) e->mins[0] = v[0];if (e->maxs[0] < v[0]) e->maxs[0] = v[0];
797                                 if (e->mins[1] > v[1]) e->mins[1] = v[1];if (e->maxs[1] < v[1]) e->maxs[1] = v[1];
798                                 if (e->mins[2] > v[2]) e->mins[2] = v[2];if (e->maxs[2] < v[2]) e->maxs[2] = v[2];
799                                 VectorSubtract(v, e->origin, temp);
800                                 dist = DotProduct(temp, temp);
801                                 if (radius2 < dist)
802                                         radius2 = dist;
803                         }
804                 }
805                 if (e->cullradius2 > radius2)
806                 {
807                         e->cullradius2 = radius2;
808                         e->cullradius = sqrt(e->cullradius2);
809                 }
810                 if (e->mins[0] < e->origin[0] - e->lightradius) e->mins[0] = e->origin[0] - e->lightradius;
811                 if (e->maxs[0] > e->origin[0] + e->lightradius) e->maxs[0] = e->origin[0] + e->lightradius;
812                 if (e->mins[1] < e->origin[1] - e->lightradius) e->mins[1] = e->origin[1] - e->lightradius;
813                 if (e->maxs[1] > e->origin[1] + e->lightradius) e->maxs[1] = e->origin[1] + e->lightradius;
814                 if (e->mins[2] < e->origin[2] - e->lightradius) e->mins[2] = e->origin[2] - e->lightradius;
815                 if (e->maxs[2] > e->origin[2] + e->lightradius) e->maxs[2] = e->origin[2] + e->lightradius;
816                 // clip shadow volumes against eachother to remove unnecessary
817                 // polygons(and sections of polygons)
818                 {
819                         //vec3_t polymins, polymaxs;
820                         int maxverts = 4;
821                         float *verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
822                         float f, *v0, *v1, projectdistance;
823
824                         e->shadowvolume = Mod_ShadowMesh_Begin(loadmodel->mempool, 1024);
825 #if 0
826                         {
827                         vec3_t outermins, outermaxs, innermins, innermaxs;
828                         innermins[0] = e->mins[0] - 1;
829                         innermins[1] = e->mins[1] - 1;
830                         innermins[2] = e->mins[2] - 1;
831                         innermaxs[0] = e->maxs[0] + 1;
832                         innermaxs[1] = e->maxs[1] + 1;
833                         innermaxs[2] = e->maxs[2] + 1;
834                         outermins[0] = loadmodel->normalmins[0] - 1;
835                         outermins[1] = loadmodel->normalmins[1] - 1;
836                         outermins[2] = loadmodel->normalmins[2] - 1;
837                         outermaxs[0] = loadmodel->normalmaxs[0] + 1;
838                         outermaxs[1] = loadmodel->normalmaxs[1] + 1;
839                         outermaxs[2] = loadmodel->normalmaxs[2] + 1;
840                         // add bounding box around the whole shadow volume set,
841                         // facing inward to limit light area, with an outer bounding box
842                         // facing outward (this is needed by the shadow rendering method)
843                         // X major
844                         verts[ 0] = innermaxs[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
845                         verts[ 3] = innermaxs[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
846                         verts[ 6] = innermaxs[0];verts[ 7] = innermaxs[1];verts[ 8] = innermins[2];
847                         verts[ 9] = innermaxs[0];verts[10] = innermaxs[1];verts[11] = innermaxs[2];
848                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
849                         verts[ 0] = outermaxs[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
850                         verts[ 3] = outermaxs[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
851                         verts[ 6] = outermaxs[0];verts[ 7] = outermins[1];verts[ 8] = outermins[2];
852                         verts[ 9] = outermaxs[0];verts[10] = outermins[1];verts[11] = outermaxs[2];
853                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
854                         // X minor
855                         verts[ 0] = innermins[0];verts[ 1] = innermaxs[1];verts[ 2] = innermaxs[2];
856                         verts[ 3] = innermins[0];verts[ 4] = innermaxs[1];verts[ 5] = innermins[2];
857                         verts[ 6] = innermins[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
858                         verts[ 9] = innermins[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
859                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
860                         verts[ 0] = outermins[0];verts[ 1] = outermins[1];verts[ 2] = outermaxs[2];
861                         verts[ 3] = outermins[0];verts[ 4] = outermins[1];verts[ 5] = outermins[2];
862                         verts[ 6] = outermins[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
863                         verts[ 9] = outermins[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
864                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
865                         // Y major
866                         verts[ 0] = innermaxs[0];verts[ 1] = innermaxs[1];verts[ 2] = innermaxs[2];
867                         verts[ 3] = innermaxs[0];verts[ 4] = innermaxs[1];verts[ 5] = innermins[2];
868                         verts[ 6] = innermins[0];verts[ 7] = innermaxs[1];verts[ 8] = innermins[2];
869                         verts[ 9] = innermins[0];verts[10] = innermaxs[1];verts[11] = innermaxs[2];
870                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
871                         verts[ 0] = outermins[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
872                         verts[ 3] = outermins[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
873                         verts[ 6] = outermaxs[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
874                         verts[ 9] = outermaxs[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
875                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
876                         // Y minor
877                         verts[ 0] = innermins[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
878                         verts[ 3] = innermins[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
879                         verts[ 6] = innermaxs[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
880                         verts[ 9] = innermaxs[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
881                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
882                         verts[ 0] = outermaxs[0];verts[ 1] = outermins[1];verts[ 2] = outermaxs[2];
883                         verts[ 3] = outermaxs[0];verts[ 4] = outermins[1];verts[ 5] = outermins[2];
884                         verts[ 6] = outermins[0];verts[ 7] = outermins[1];verts[ 8] = outermins[2];
885                         verts[ 9] = outermins[0];verts[10] = outermins[1];verts[11] = outermaxs[2];
886                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
887                         // Z major
888                         verts[ 0] = innermaxs[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
889                         verts[ 3] = innermaxs[0];verts[ 4] = innermaxs[1];verts[ 5] = innermaxs[2];
890                         verts[ 6] = innermins[0];verts[ 7] = innermaxs[1];verts[ 8] = innermaxs[2];
891                         verts[ 9] = innermins[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
892                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
893                         verts[ 0] = outermaxs[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
894                         verts[ 3] = outermaxs[0];verts[ 4] = outermins[1];verts[ 5] = outermaxs[2];
895                         verts[ 6] = outermins[0];verts[ 7] = outermins[1];verts[ 8] = outermaxs[2];
896                         verts[ 9] = outermins[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
897                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
898                         // Z minor
899                         verts[ 0] = innermaxs[0];verts[ 1] = innermaxs[1];verts[ 2] = innermins[2];
900                         verts[ 3] = innermaxs[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
901                         verts[ 6] = innermins[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
902                         verts[ 9] = innermins[0];verts[10] = innermaxs[1];verts[11] = innermins[2];
903                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
904                         verts[ 0] = outermaxs[0];verts[ 1] = outermins[1];verts[ 2] = outermins[2];
905                         verts[ 3] = outermaxs[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
906                         verts[ 6] = outermins[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
907                         verts[ 9] = outermins[0];verts[10] = outermins[1];verts[11] = outermins[2];
908                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
909                         }
910 #endif
911                         castshadowcount++;
912                         for (j = 0;j < e->numsurfaces;j++)
913                         {
914                                 surf = e->surfaces[j];
915                                 if (surf->flags & SURF_SHADOWCAST)
916                                         surf->castshadow = castshadowcount;
917                         }
918                         for (j = 0;j < e->numsurfaces;j++)
919                         {
920                                 surf = e->surfaces[j];
921                                 if (surf->castshadow != castshadowcount)
922                                         continue;
923                                 f = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
924                                 if (surf->flags & SURF_PLANEBACK)
925                                         f = -f;
926                                 projectdistance = e->lightradius;
927                                 if (maxverts < surf->poly_numverts)
928                                 {
929                                         maxverts = surf->poly_numverts;
930                                         if (verts)
931                                                 Mem_Free(verts);
932                                         verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
933                                 }
934                                 // copy the original polygon, for the front cap of the volume
935                                 for (k = 0, v0 = surf->poly_verts, v1 = verts;k < surf->poly_numverts;k++, v0 += 3, v1 += 3)
936                                         VectorCopy(v0, v1);
937                                 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, surf->poly_numverts, verts);
938                                 // project the original polygon, reversed, for the back cap of the volume
939                                 for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = verts;k < surf->poly_numverts;k++, v0 -= 3, v1 += 3)
940                                 {
941                                         VectorSubtract(v0, e->origin, temp);
942                                         VectorNormalize(temp);
943                                         VectorMA(v0, projectdistance, temp, v1);
944                                 }
945                                 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, surf->poly_numverts, verts);
946                                 // project the shadow volume sides
947                                 for (l = surf->poly_numverts - 1, k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;k < surf->poly_numverts;l = k, k++, v0 = v1, v1 += 3)
948                                 {
949                                         if (!surf->neighborsurfaces[l] || surf->neighborsurfaces[l]->castshadow != castshadowcount)
950                                         {
951                                                 VectorCopy(v1, &verts[0]);
952                                                 VectorCopy(v0, &verts[3]);
953                                                 VectorCopy(v0, &verts[6]);
954                                                 VectorCopy(v1, &verts[9]);
955                                                 VectorSubtract(&verts[6], e->origin, temp);
956                                                 VectorNormalize(temp);
957                                                 VectorMA(&verts[6], projectdistance, temp, &verts[6]);
958                                                 VectorSubtract(&verts[9], e->origin, temp);
959                                                 VectorNormalize(temp);
960                                                 VectorMA(&verts[9], projectdistance, temp, &verts[9]);
961                                                 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
962                                         }
963                                 }
964                         }
965                         // build the triangle mesh
966                         e->shadowvolume = Mod_ShadowMesh_Finish(loadmodel->mempool, e->shadowvolume);
967                         {
968                                 shadowmesh_t *mesh;
969                                 l = 0;
970                                 for (mesh = e->shadowvolume;mesh;mesh = mesh->next)
971                                         l += mesh->numtriangles;
972                                 Con_Printf("light %i shadow volume built containing %i triangles\n", lnum, l);
973                         }
974                 }
975         }
976 }
977 */
978
979
980 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
981 {
982         loadmodel->visdata = NULL;
983         if (!l->filelen)
984                 return;
985         loadmodel->visdata = Mem_Alloc(loadmodel->mempool, l->filelen);
986         memcpy(loadmodel->visdata, mod_base + l->fileofs, l->filelen);
987 }
988
989 // used only for HalfLife maps
990 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
991 {
992         char key[128], value[4096];
993         char wadname[128];
994         int i, j, k;
995         if (!data)
996                 return;
997         if (!COM_ParseToken(&data))
998                 return; // error
999         if (com_token[0] != '{')
1000                 return; // error
1001         while (1)
1002         {
1003                 if (!COM_ParseToken(&data))
1004                         return; // error
1005                 if (com_token[0] == '}')
1006                         break; // end of worldspawn
1007                 if (com_token[0] == '_')
1008                         strcpy(key, com_token + 1);
1009                 else
1010                         strcpy(key, com_token);
1011                 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1012                         key[strlen(key)-1] = 0;
1013                 if (!COM_ParseToken(&data))
1014                         return; // error
1015                 strcpy(value, com_token);
1016                 if (!strcmp("wad", key)) // for HalfLife maps
1017                 {
1018                         if (loadmodel->ishlbsp)
1019                         {
1020                                 j = 0;
1021                                 for (i = 0;i < 4096;i++)
1022                                         if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1023                                                 break;
1024                                 if (value[i])
1025                                 {
1026                                         for (;i < 4096;i++)
1027                                         {
1028                                                 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1029                                                 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1030                                                         j = i+1;
1031                                                 else if (value[i] == ';' || value[i] == 0)
1032                                                 {
1033                                                         k = value[i];
1034                                                         value[i] = 0;
1035                                                         strcpy(wadname, "textures/");
1036                                                         strcat(wadname, &value[j]);
1037                                                         W_LoadTextureWadFile(wadname, false);
1038                                                         j = i+1;
1039                                                         if (!k)
1040                                                                 break;
1041                                                 }
1042                                         }
1043                                 }
1044                         }
1045                 }
1046         }
1047 }
1048
1049 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1050 {
1051         loadmodel->entities = NULL;
1052         if (!l->filelen)
1053                 return;
1054         loadmodel->entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1055         memcpy(loadmodel->entities, mod_base + l->fileofs, l->filelen);
1056         if (loadmodel->ishlbsp)
1057                 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->entities);
1058 }
1059
1060
1061 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1062 {
1063         dvertex_t       *in;
1064         mvertex_t       *out;
1065         int                     i, count;
1066
1067         in = (void *)(mod_base + l->fileofs);
1068         if (l->filelen % sizeof(*in))
1069                 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1070         count = l->filelen / sizeof(*in);
1071         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1072
1073         loadmodel->vertexes = out;
1074         loadmodel->numvertexes = count;
1075
1076         for ( i=0 ; i<count ; i++, in++, out++)
1077         {
1078                 out->position[0] = LittleFloat(in->point[0]);
1079                 out->position[1] = LittleFloat(in->point[1]);
1080                 out->position[2] = LittleFloat(in->point[2]);
1081         }
1082 }
1083
1084 static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
1085 {
1086         dmodel_t        *in;
1087         dmodel_t        *out;
1088         int                     i, j, count;
1089
1090         in = (void *)(mod_base + l->fileofs);
1091         if (l->filelen % sizeof(*in))
1092                 Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
1093         count = l->filelen / sizeof(*in);
1094         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1095
1096         loadmodel->submodels = out;
1097         loadmodel->numsubmodels = count;
1098
1099         for ( i=0 ; i<count ; i++, in++, out++)
1100         {
1101                 for (j=0 ; j<3 ; j++)
1102                 {
1103                         // spread the mins / maxs by a pixel
1104                         out->mins[j] = LittleFloat(in->mins[j]) - 1;
1105                         out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
1106                         out->origin[j] = LittleFloat(in->origin[j]);
1107                 }
1108                 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1109                         out->headnode[j] = LittleLong(in->headnode[j]);
1110                 out->visleafs = LittleLong(in->visleafs);
1111                 out->firstface = LittleLong(in->firstface);
1112                 out->numfaces = LittleLong(in->numfaces);
1113         }
1114 }
1115
1116 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1117 {
1118         dedge_t *in;
1119         medge_t *out;
1120         int     i, count;
1121
1122         in = (void *)(mod_base + l->fileofs);
1123         if (l->filelen % sizeof(*in))
1124                 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1125         count = l->filelen / sizeof(*in);
1126         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1127
1128         loadmodel->edges = out;
1129         loadmodel->numedges = count;
1130
1131         for ( i=0 ; i<count ; i++, in++, out++)
1132         {
1133                 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1134                 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1135         }
1136 }
1137
1138 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1139 {
1140         texinfo_t *in;
1141         mtexinfo_t *out;
1142         int i, j, k, count, miptex;
1143
1144         in = (void *)(mod_base + l->fileofs);
1145         if (l->filelen % sizeof(*in))
1146                 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1147         count = l->filelen / sizeof(*in);
1148         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1149
1150         loadmodel->texinfo = out;
1151         loadmodel->numtexinfo = count;
1152
1153         for (i = 0;i < count;i++, in++, out++)
1154         {
1155                 for (k = 0;k < 2;k++)
1156                         for (j = 0;j < 4;j++)
1157                                 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1158
1159                 miptex = LittleLong(in->miptex);
1160                 out->flags = LittleLong(in->flags);
1161
1162                 out->texture = NULL;
1163                 if (loadmodel->textures)
1164                 {
1165                         if ((unsigned int) miptex >= (unsigned int) loadmodel->numtextures)
1166                                 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->numtextures);
1167                         else
1168                                 out->texture = loadmodel->textures + miptex;
1169                 }
1170                 if (out->flags & TEX_SPECIAL)
1171                 {
1172                         // if texture chosen is NULL or the shader needs a lightmap,
1173                         // force to notexture water shader
1174                         if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
1175                                 out->texture = loadmodel->textures + (loadmodel->numtextures - 1);
1176                 }
1177                 else
1178                 {
1179                         // if texture chosen is NULL, force to notexture
1180                         if (out->texture == NULL)
1181                                 out->texture = loadmodel->textures + (loadmodel->numtextures - 2);
1182                 }
1183         }
1184 }
1185
1186 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1187 {
1188         int             i, j;
1189         float   *v;
1190
1191         mins[0] = mins[1] = mins[2] = 9999;
1192         maxs[0] = maxs[1] = maxs[2] = -9999;
1193         v = verts;
1194         for (i = 0;i < numverts;i++)
1195         {
1196                 for (j = 0;j < 3;j++, v++)
1197                 {
1198                         if (*v < mins[j])
1199                                 mins[j] = *v;
1200                         if (*v > maxs[j])
1201                                 maxs[j] = *v;
1202                 }
1203         }
1204 }
1205
1206 #if 0
1207 #define MAX_SUBDIVPOLYTRIANGLES 4096
1208 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1209
1210 static int subdivpolyverts, subdivpolytriangles;
1211 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1212 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1213
1214 static int subdivpolylookupvert(vec3_t v)
1215 {
1216         int i;
1217         for (i = 0;i < subdivpolyverts;i++)
1218                 if (subdivpolyvert[i][0] == v[0]
1219                  && subdivpolyvert[i][1] == v[1]
1220                  && subdivpolyvert[i][2] == v[2])
1221                         return i;
1222         if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1223                 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1224         VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1225         return subdivpolyverts++;
1226 }
1227
1228 static void SubdividePolygon(int numverts, float *verts)
1229 {
1230         int             i, i1, i2, i3, f, b, c, p;
1231         vec3_t  mins, maxs, front[256], back[256];
1232         float   m, *pv, *cv, dist[256], frac;
1233
1234         if (numverts > 250)
1235                 Host_Error("SubdividePolygon: ran out of verts in buffer");
1236
1237         BoundPoly(numverts, verts, mins, maxs);
1238
1239         for (i = 0;i < 3;i++)
1240         {
1241                 m = (mins[i] + maxs[i]) * 0.5;
1242                 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1243                 if (maxs[i] - m < 8)
1244                         continue;
1245                 if (m - mins[i] < 8)
1246                         continue;
1247
1248                 // cut it
1249                 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1250                         dist[c] = cv[i] - m;
1251
1252                 f = b = 0;
1253                 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1254                 {
1255                         if (dist[p] >= 0)
1256                         {
1257                                 VectorCopy(pv, front[f]);
1258                                 f++;
1259                         }
1260                         if (dist[p] <= 0)
1261                         {
1262                                 VectorCopy(pv, back[b]);
1263                                 b++;
1264                         }
1265                         if (dist[p] == 0 || dist[c] == 0)
1266                                 continue;
1267                         if ((dist[p] > 0) != (dist[c] > 0) )
1268                         {
1269                                 // clip point
1270                                 frac = dist[p] / (dist[p] - dist[c]);
1271                                 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1272                                 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1273                                 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1274                                 f++;
1275                                 b++;
1276                         }
1277                 }
1278
1279                 SubdividePolygon(f, front[0]);
1280                 SubdividePolygon(b, back[0]);
1281                 return;
1282         }
1283
1284         i1 = subdivpolylookupvert(verts);
1285         i2 = subdivpolylookupvert(verts + 3);
1286         for (i = 2;i < numverts;i++)
1287         {
1288                 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1289                 {
1290                         Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1291                         return;
1292                 }
1293
1294                 i3 = subdivpolylookupvert(verts + i * 3);
1295                 subdivpolyindex[subdivpolytriangles][0] = i1;
1296                 subdivpolyindex[subdivpolytriangles][1] = i2;
1297                 subdivpolyindex[subdivpolytriangles][2] = i3;
1298                 i2 = i3;
1299                 subdivpolytriangles++;
1300         }
1301 }
1302
1303 //Breaks a polygon up along axial 64 unit
1304 //boundaries so that turbulent and sky warps
1305 //can be done reasonably.
1306 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
1307 {
1308         int i, j;
1309         surfvertex_t *v;
1310         surfmesh_t *mesh;
1311
1312         subdivpolytriangles = 0;
1313         subdivpolyverts = 0;
1314         SubdividePolygon(surf->poly_numverts, surf->poly_verts);
1315         if (subdivpolytriangles < 1)
1316                 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
1317
1318         surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1319         mesh->numverts = subdivpolyverts;
1320         mesh->numtriangles = subdivpolytriangles;
1321         mesh->vertex = (surfvertex_t *)(mesh + 1);
1322         mesh->index = (int *)(mesh->vertex + mesh->numverts);
1323         memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1324
1325         for (i = 0;i < mesh->numtriangles;i++)
1326                 for (j = 0;j < 3;j++)
1327                         mesh->index[i*3+j] = subdivpolyindex[i][j];
1328
1329         for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1330         {
1331                 VectorCopy(subdivpolyvert[i], v->v);
1332                 v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]);
1333                 v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]);
1334         }
1335 }
1336 #endif
1337
1338 static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles)
1339 {
1340         surfmesh_t *mesh;
1341         mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float));
1342         mesh->numverts = numverts;
1343         mesh->numtriangles = numtriangles;
1344         mesh->vertex3f = (float *)(mesh + 1);
1345         mesh->texcoordtexture2f = mesh->vertex3f + mesh->numverts * 3;
1346         mesh->texcoordlightmap2f = mesh->texcoordtexture2f + mesh->numverts * 2;
1347         mesh->texcoorddetail2f = mesh->texcoordlightmap2f + mesh->numverts * 2;
1348         mesh->svector3f = (float *)(mesh->texcoorddetail2f + mesh->numverts * 2);
1349         mesh->tvector3f = mesh->svector3f + mesh->numverts * 3;
1350         mesh->normal3f = mesh->tvector3f + mesh->numverts * 3;
1351         mesh->lightmapoffsets = (int *)(mesh->normal3f + mesh->numverts * 3);
1352         mesh->element3i = mesh->lightmapoffsets + mesh->numverts;
1353         mesh->neighbor3i = mesh->element3i + mesh->numtriangles * 3;
1354         return mesh;
1355 }
1356
1357 static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges)
1358 {
1359         int i, lindex, j;
1360         float *vec, *vert, mins[3], maxs[3], val, *v;
1361         mtexinfo_t *tex;
1362
1363         // convert edges back to a normal polygon
1364         surf->poly_numverts = numedges;
1365         vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges);
1366         for (i = 0;i < numedges;i++)
1367         {
1368                 lindex = loadmodel->surfedges[firstedge + i];
1369                 if (lindex > 0)
1370                         vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
1371                 else
1372                         vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
1373                 VectorCopy(vec, vert);
1374                 vert += 3;
1375         }
1376
1377         // calculate polygon bounding box and center
1378         vert = surf->poly_verts;
1379         VectorCopy(vert, mins);
1380         VectorCopy(vert, maxs);
1381         vert += 3;
1382         for (i = 1;i < surf->poly_numverts;i++, vert += 3)
1383         {
1384                 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1385                 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1386                 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1387         }
1388         VectorCopy(mins, surf->poly_mins);
1389         VectorCopy(maxs, surf->poly_maxs);
1390         surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1391         surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1392         surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1393
1394         // generate surface extents information
1395         tex = surf->texinfo;
1396         mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3];
1397         mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3];
1398         for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3)
1399         {
1400                 for (j = 0;j < 2;j++)
1401                 {
1402                         val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3];
1403                         if (mins[j] > val)
1404                                 mins[j] = val;
1405                         if (maxs[j] < val)
1406                                 maxs[j] = val;
1407                 }
1408         }
1409         for (i = 0;i < 2;i++)
1410         {
1411                 surf->texturemins[i] = (int) floor(mins[i] / 16) * 16;
1412                 surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i];
1413         }
1414 }
1415
1416 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1417 {
1418         dface_t *in;
1419         msurface_t *surf;
1420         int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes;
1421         surfmesh_t *mesh;
1422         float s, t;
1423
1424         in = (void *)(mod_base + l->fileofs);
1425         if (l->filelen % sizeof(*in))
1426                 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1427         count = l->filelen / sizeof(*in);
1428         loadmodel->surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1429
1430         loadmodel->numsurfaces = count;
1431         loadmodel->surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1432         loadmodel->surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1433         loadmodel->pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1434
1435         for (surfnum = 0, surf = loadmodel->surfaces, totalverts = 0, totaltris = 0, totalmeshes = 0;surfnum < count;surfnum++, totalverts += surf->poly_numverts, totaltris += surf->poly_numverts - 2, totalmeshes++, in++, surf++)
1436         {
1437                 surf->number = surfnum;
1438                 // FIXME: validate edges, texinfo, etc?
1439                 firstedge = LittleLong(in->firstedge);
1440                 numedges = LittleShort(in->numedges);
1441                 if ((unsigned int) firstedge > (unsigned int) loadmodel->numsurfedges || (unsigned int) numedges > (unsigned int) loadmodel->numsurfedges || (unsigned int) firstedge + (unsigned int) numedges > (unsigned int) loadmodel->numsurfedges)
1442                         Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->numsurfedges);
1443                 i = LittleShort(in->texinfo);
1444                 if ((unsigned int) i >= (unsigned int) loadmodel->numtexinfo)
1445                         Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->numtexinfo);
1446                 surf->texinfo = loadmodel->texinfo + i;
1447                 surf->flags = surf->texinfo->texture->flags;
1448
1449                 planenum = LittleShort(in->planenum);
1450                 if ((unsigned int) planenum >= (unsigned int) loadmodel->numplanes)
1451                         Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->numplanes);
1452
1453                 if (LittleShort(in->side))
1454                         surf->flags |= SURF_PLANEBACK;
1455
1456                 surf->plane = loadmodel->planes + planenum;
1457
1458                 // clear lightmap (filled in later)
1459                 surf->lightmaptexture = NULL;
1460
1461                 // force lightmap upload on first time seeing the surface
1462                 surf->cached_dlight = true;
1463
1464                 Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges);
1465
1466                 ssize = (surf->extents[0] >> 4) + 1;
1467                 tsize = (surf->extents[1] >> 4) + 1;
1468
1469                 // lighting info
1470                 for (i = 0;i < MAXLIGHTMAPS;i++)
1471                         surf->styles[i] = in->styles[i];
1472                 i = LittleLong(in->lightofs);
1473                 if (i == -1)
1474                         surf->samples = NULL;
1475                 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1476                         surf->samples = loadmodel->lightdata + i;
1477                 else // LordHavoc: white lighting (bsp version 29)
1478                         surf->samples = loadmodel->lightdata + (i * 3);
1479
1480                 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1481                 {
1482                         if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256))
1483                                 Host_Error("Bad surface extents");
1484                         // stainmap for permanent marks on walls
1485                         surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1486                         // clear to white
1487                         memset(surf->stainsamples, 255, ssize * tsize * 3);
1488                 }
1489         }
1490
1491         loadmodel->entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris);
1492         loadmodel->surfmeshes = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) * totalmeshes);
1493
1494         for (surfnum = 0, surf = loadmodel->surfaces, totalverts = 0, totaltris = 0, totalmeshes = 0;surfnum < count;surfnum++, totalverts += surf->poly_numverts, totaltris += surf->poly_numverts - 2, totalmeshes++, surf++)
1495         {
1496                 mesh = surf->mesh = loadmodel->surfmeshes + totalmeshes;
1497                 mesh->numverts = surf->poly_numverts;
1498                 mesh->numtriangles = surf->poly_numverts - 2;
1499                 mesh->vertex3f = loadmodel->entiremesh->vertex3f + totalverts * 3;
1500                 mesh->texcoordtexture2f = loadmodel->entiremesh->texcoordtexture2f + totalverts * 2;
1501                 mesh->texcoordlightmap2f = loadmodel->entiremesh->texcoordlightmap2f + totalverts * 2;
1502                 mesh->texcoorddetail2f = loadmodel->entiremesh->texcoorddetail2f + totalverts * 2;
1503                 mesh->svector3f = loadmodel->entiremesh->svector3f + totalverts * 3;
1504                 mesh->tvector3f = loadmodel->entiremesh->tvector3f + totalverts * 3;
1505                 mesh->normal3f = loadmodel->entiremesh->normal3f + totalverts * 3;
1506                 mesh->lightmapoffsets = loadmodel->entiremesh->lightmapoffsets + totalverts;
1507                 mesh->element3i = loadmodel->entiremesh->element3i + totaltris * 3;
1508                 mesh->neighbor3i = loadmodel->entiremesh->neighbor3i + totaltris * 3;
1509
1510                 surf->lightmaptexturestride = 0;
1511                 surf->lightmaptexture = NULL;
1512
1513                 for (i = 0;i < mesh->numverts;i++)
1514                 {
1515                         mesh->vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0];
1516                         mesh->vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1];
1517                         mesh->vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2];
1518                         s = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1519                         t = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1520                         mesh->texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width;
1521                         mesh->texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height;
1522                         mesh->texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
1523                         mesh->texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
1524                         mesh->texcoordlightmap2f[i * 2 + 0] = 0;
1525                         mesh->texcoordlightmap2f[i * 2 + 1] = 0;
1526                         mesh->lightmapoffsets[i] = 0;
1527                 }
1528
1529                 for (i = 0;i < mesh->numtriangles;i++)
1530                 {
1531                         mesh->element3i[i * 3 + 0] = 0;
1532                         mesh->element3i[i * 3 + 1] = i + 1;
1533                         mesh->element3i[i * 3 + 2] = i + 2;
1534                 }
1535
1536                 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
1537                 Mod_BuildTextureVectorsAndNormals(mesh->numverts, mesh->numtriangles, mesh->vertex3f, mesh->texcoordtexture2f, mesh->element3i, mesh->svector3f, mesh->tvector3f, mesh->normal3f);
1538
1539                 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1540                 {
1541                         int i, iu, iv, smax, tmax;
1542                         float u, v, ubase, vbase, uscale, vscale;
1543
1544                         smax = surf->extents[0] >> 4;
1545                         tmax = surf->extents[1] >> 4;
1546
1547                         surf->flags |= SURF_LIGHTMAP;
1548                         if (r_miplightmaps.integer)
1549                         {
1550                                 surf->lightmaptexturestride = smax+1;
1551                                 surf->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_MIPMAP | TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
1552                         }
1553                         else
1554                         {
1555                                 surf->lightmaptexturestride = R_CompatibleFragmentWidth(smax+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1556                                 surf->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FRAGMENT | TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
1557                         }
1558                         R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1559                         uscale = (uscale - ubase) / (smax + 1);
1560                         vscale = (vscale - vbase) / (tmax + 1);
1561
1562                         for (i = 0;i < mesh->numverts;i++)
1563                         {
1564                                 u = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1565                                 v = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1566                                 mesh->texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
1567                                 mesh->texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase;
1568                                 // LordHavoc: calc lightmap data offset for vertex lighting to use
1569                                 iu = (int) u;
1570                                 iv = (int) v;
1571                                 mesh->lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3;
1572                         }
1573                 }
1574         }
1575 }
1576
1577 static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent)
1578 {
1579         node->parent = parent;
1580         if (node->contents < 0)
1581                 return;
1582         Mod_Q1BSP_SetParent(node->children[0], node);
1583         Mod_Q1BSP_SetParent(node->children[1], node);
1584 }
1585
1586 static void Mod_Q1BSP_LoadNodes(lump_t *l)
1587 {
1588         int                     i, j, count, p;
1589         dnode_t         *in;
1590         mnode_t         *out;
1591
1592         in = (void *)(mod_base + l->fileofs);
1593         if (l->filelen % sizeof(*in))
1594                 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
1595         count = l->filelen / sizeof(*in);
1596         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1597
1598         loadmodel->nodes = out;
1599         loadmodel->numnodes = count;
1600
1601         for ( i=0 ; i<count ; i++, in++, out++)
1602         {
1603                 for (j=0 ; j<3 ; j++)
1604                 {
1605                         out->mins[j] = LittleShort(in->mins[j]);
1606                         out->maxs[j] = LittleShort(in->maxs[j]);
1607                 }
1608
1609                 p = LittleLong(in->planenum);
1610                 out->plane = loadmodel->planes + p;
1611
1612                 out->firstsurface = LittleShort(in->firstface);
1613                 out->numsurfaces = LittleShort(in->numfaces);
1614
1615                 for (j=0 ; j<2 ; j++)
1616                 {
1617                         p = LittleShort(in->children[j]);
1618                         if (p >= 0)
1619                                 out->children[j] = loadmodel->nodes + p;
1620                         else
1621                                 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1622                 }
1623         }
1624
1625         Mod_Q1BSP_SetParent(loadmodel->nodes, NULL);    // sets nodes and leafs
1626 }
1627
1628 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
1629 {
1630         dleaf_t         *in;
1631         mleaf_t         *out;
1632         int                     i, j, count, p;
1633
1634         in = (void *)(mod_base + l->fileofs);
1635         if (l->filelen % sizeof(*in))
1636                 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
1637         count = l->filelen / sizeof(*in);
1638         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1639
1640         loadmodel->leafs = out;
1641         loadmodel->numleafs = count;
1642
1643         for ( i=0 ; i<count ; i++, in++, out++)
1644         {
1645                 for (j=0 ; j<3 ; j++)
1646                 {
1647                         out->mins[j] = LittleShort(in->mins[j]);
1648                         out->maxs[j] = LittleShort(in->maxs[j]);
1649                 }
1650
1651                 p = LittleLong(in->contents);
1652                 out->contents = p;
1653
1654                 out->firstmarksurface = loadmodel->marksurfaces +
1655                         LittleShort(in->firstmarksurface);
1656                 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1657
1658                 p = LittleLong(in->visofs);
1659                 if (p == -1)
1660                         out->compressed_vis = NULL;
1661                 else
1662                         out->compressed_vis = loadmodel->visdata + p;
1663
1664                 for (j=0 ; j<4 ; j++)
1665                         out->ambient_sound_level[j] = in->ambient_level[j];
1666
1667                 // FIXME: Insert caustics here
1668         }
1669 }
1670
1671 static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
1672 {
1673         dclipnode_t *in, *out;
1674         int                     i, count;
1675         hull_t          *hull;
1676
1677         in = (void *)(mod_base + l->fileofs);
1678         if (l->filelen % sizeof(*in))
1679                 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
1680         count = l->filelen / sizeof(*in);
1681         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1682
1683         loadmodel->clipnodes = out;
1684         loadmodel->numclipnodes = count;
1685
1686         if (loadmodel->ishlbsp)
1687         {
1688                 hull = &loadmodel->hulls[1];
1689                 hull->clipnodes = out;
1690                 hull->firstclipnode = 0;
1691                 hull->lastclipnode = count-1;
1692                 hull->planes = loadmodel->planes;
1693                 hull->clip_mins[0] = -16;
1694                 hull->clip_mins[1] = -16;
1695                 hull->clip_mins[2] = -36;
1696                 hull->clip_maxs[0] = 16;
1697                 hull->clip_maxs[1] = 16;
1698                 hull->clip_maxs[2] = 36;
1699                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1700
1701                 hull = &loadmodel->hulls[2];
1702                 hull->clipnodes = out;
1703                 hull->firstclipnode = 0;
1704                 hull->lastclipnode = count-1;
1705                 hull->planes = loadmodel->planes;
1706                 hull->clip_mins[0] = -32;
1707                 hull->clip_mins[1] = -32;
1708                 hull->clip_mins[2] = -32;
1709                 hull->clip_maxs[0] = 32;
1710                 hull->clip_maxs[1] = 32;
1711                 hull->clip_maxs[2] = 32;
1712                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1713
1714                 hull = &loadmodel->hulls[3];
1715                 hull->clipnodes = out;
1716                 hull->firstclipnode = 0;
1717                 hull->lastclipnode = count-1;
1718                 hull->planes = loadmodel->planes;
1719                 hull->clip_mins[0] = -16;
1720                 hull->clip_mins[1] = -16;
1721                 hull->clip_mins[2] = -18;
1722                 hull->clip_maxs[0] = 16;
1723                 hull->clip_maxs[1] = 16;
1724                 hull->clip_maxs[2] = 18;
1725                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1726         }
1727         else
1728         {
1729                 hull = &loadmodel->hulls[1];
1730                 hull->clipnodes = out;
1731                 hull->firstclipnode = 0;
1732                 hull->lastclipnode = count-1;
1733                 hull->planes = loadmodel->planes;
1734                 hull->clip_mins[0] = -16;
1735                 hull->clip_mins[1] = -16;
1736                 hull->clip_mins[2] = -24;
1737                 hull->clip_maxs[0] = 16;
1738                 hull->clip_maxs[1] = 16;
1739                 hull->clip_maxs[2] = 32;
1740                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1741
1742                 hull = &loadmodel->hulls[2];
1743                 hull->clipnodes = out;
1744                 hull->firstclipnode = 0;
1745                 hull->lastclipnode = count-1;
1746                 hull->planes = loadmodel->planes;
1747                 hull->clip_mins[0] = -32;
1748                 hull->clip_mins[1] = -32;
1749                 hull->clip_mins[2] = -24;
1750                 hull->clip_maxs[0] = 32;
1751                 hull->clip_maxs[1] = 32;
1752                 hull->clip_maxs[2] = 64;
1753                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1754         }
1755
1756         for (i=0 ; i<count ; i++, out++, in++)
1757         {
1758                 out->planenum = LittleLong(in->planenum);
1759                 out->children[0] = LittleShort(in->children[0]);
1760                 out->children[1] = LittleShort(in->children[1]);
1761                 if (out->children[0] >= count || out->children[1] >= count)
1762                         Host_Error("Corrupt clipping hull(out of range child)\n");
1763         }
1764 }
1765
1766 //Duplicate the drawing hull structure as a clipping hull
1767 static void Mod_Q1BSP_MakeHull0(void)
1768 {
1769         mnode_t         *in;
1770         dclipnode_t *out;
1771         int                     i;
1772         hull_t          *hull;
1773
1774         hull = &loadmodel->hulls[0];
1775
1776         in = loadmodel->nodes;
1777         out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1778
1779         hull->clipnodes = out;
1780         hull->firstclipnode = 0;
1781         hull->lastclipnode = loadmodel->numnodes - 1;
1782         hull->planes = loadmodel->planes;
1783
1784         for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1785         {
1786                 out->planenum = in->plane - loadmodel->planes;
1787                 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1788                 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1789         }
1790 }
1791
1792 static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l)
1793 {
1794         int i, j;
1795         short *in;
1796
1797         in = (void *)(mod_base + l->fileofs);
1798         if (l->filelen % sizeof(*in))
1799                 Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
1800         loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1801         loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(int));
1802
1803         for (i = 0;i < loadmodel->nummarksurfaces;i++)
1804         {
1805                 j = (unsigned) LittleShort(in[i]);
1806                 if (j >= loadmodel->numsurfaces)
1807                         Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number");
1808                 loadmodel->marksurfaces[i] = j;
1809         }
1810 }
1811
1812 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
1813 {
1814         int             i;
1815         int             *in;
1816
1817         in = (void *)(mod_base + l->fileofs);
1818         if (l->filelen % sizeof(*in))
1819                 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
1820         loadmodel->numsurfedges = l->filelen / sizeof(*in);
1821         loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1822
1823         for (i = 0;i < loadmodel->numsurfedges;i++)
1824                 loadmodel->surfedges[i] = LittleLong(in[i]);
1825 }
1826
1827
1828 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
1829 {
1830         int                     i;
1831         mplane_t        *out;
1832         dplane_t        *in;
1833
1834         in = (void *)(mod_base + l->fileofs);
1835         if (l->filelen % sizeof(*in))
1836                 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
1837
1838         loadmodel->numplanes = l->filelen / sizeof(*in);
1839         loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
1840
1841         for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
1842         {
1843                 out->normal[0] = LittleFloat(in->normal[0]);
1844                 out->normal[1] = LittleFloat(in->normal[1]);
1845                 out->normal[2] = LittleFloat(in->normal[2]);
1846                 out->dist = LittleFloat(in->dist);
1847
1848                 PlaneClassify(out);
1849         }
1850 }
1851
1852 #define MAX_POINTS_ON_WINDING 64
1853
1854 typedef struct
1855 {
1856         int numpoints;
1857         int padding;
1858         double points[8][3]; // variable sized
1859 }
1860 winding_t;
1861
1862 /*
1863 ==================
1864 NewWinding
1865 ==================
1866 */
1867 static winding_t *NewWinding(int points)
1868 {
1869         winding_t *w;
1870         int size;
1871
1872         if (points > MAX_POINTS_ON_WINDING)
1873                 Sys_Error("NewWinding: too many points\n");
1874
1875         size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1876         w = Mem_Alloc(loadmodel->mempool, size);
1877         memset(w, 0, size);
1878
1879         return w;
1880 }
1881
1882 static void FreeWinding(winding_t *w)
1883 {
1884         Mem_Free(w);
1885 }
1886
1887 /*
1888 =================
1889 BaseWindingForPlane
1890 =================
1891 */
1892 static winding_t *BaseWindingForPlane(mplane_t *p)
1893 {
1894         double org[3], vright[3], vup[3], normal[3];
1895         winding_t *w;
1896
1897         VectorCopy(p->normal, normal);
1898         VectorVectorsDouble(normal, vright, vup);
1899
1900         VectorScale(vup, 1024.0*1024.0*1024.0, vup);
1901         VectorScale(vright, 1024.0*1024.0*1024.0, vright);
1902
1903         // project a really big axis aligned box onto the plane
1904         w = NewWinding(4);
1905
1906         VectorScale(p->normal, p->dist, org);
1907
1908         VectorSubtract(org, vright, w->points[0]);
1909         VectorAdd(w->points[0], vup, w->points[0]);
1910
1911         VectorAdd(org, vright, w->points[1]);
1912         VectorAdd(w->points[1], vup, w->points[1]);
1913
1914         VectorAdd(org, vright, w->points[2]);
1915         VectorSubtract(w->points[2], vup, w->points[2]);
1916
1917         VectorSubtract(org, vright, w->points[3]);
1918         VectorSubtract(w->points[3], vup, w->points[3]);
1919
1920         w->numpoints = 4;
1921
1922         return w;
1923 }
1924
1925 /*
1926 ==================
1927 ClipWinding
1928
1929 Clips the winding to the plane, returning the new winding on the positive side
1930 Frees the input winding.
1931 If keepon is true, an exactly on-plane winding will be saved, otherwise
1932 it will be clipped away.
1933 ==================
1934 */
1935 static winding_t *ClipWinding(winding_t *in, mplane_t *split, int keepon)
1936 {
1937         double  dists[MAX_POINTS_ON_WINDING + 1];
1938         int             sides[MAX_POINTS_ON_WINDING + 1];
1939         int             counts[3];
1940         double  dot;
1941         int             i, j;
1942         double  *p1, *p2;
1943         double  mid[3];
1944         winding_t       *neww;
1945         int             maxpts;
1946
1947         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1948
1949         // determine sides for each point
1950         for (i = 0;i < in->numpoints;i++)
1951         {
1952                 dists[i] = dot = DotProduct(in->points[i], split->normal) - split->dist;
1953                 if (dot > ON_EPSILON)
1954                         sides[i] = SIDE_FRONT;
1955                 else if (dot < -ON_EPSILON)
1956                         sides[i] = SIDE_BACK;
1957                 else
1958                         sides[i] = SIDE_ON;
1959                 counts[sides[i]]++;
1960         }
1961         sides[i] = sides[0];
1962         dists[i] = dists[0];
1963
1964         if (keepon && !counts[0] && !counts[1])
1965                 return in;
1966
1967         if (!counts[0])
1968         {
1969                 FreeWinding(in);
1970                 return NULL;
1971         }
1972         if (!counts[1])
1973                 return in;
1974
1975         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
1976         if (maxpts > MAX_POINTS_ON_WINDING)
1977                 Sys_Error("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1978
1979         neww = NewWinding(maxpts);
1980
1981         for (i = 0;i < in->numpoints;i++)
1982         {
1983                 if (neww->numpoints >= maxpts)
1984                         Sys_Error("ClipWinding: points exceeded estimate");
1985
1986                 p1 = in->points[i];
1987
1988                 if (sides[i] == SIDE_ON)
1989                 {
1990                         VectorCopy(p1, neww->points[neww->numpoints]);
1991                         neww->numpoints++;
1992                         continue;
1993                 }
1994
1995                 if (sides[i] == SIDE_FRONT)
1996                 {
1997                         VectorCopy(p1, neww->points[neww->numpoints]);
1998                         neww->numpoints++;
1999                 }
2000
2001                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2002                         continue;
2003
2004                 // generate a split point
2005                 p2 = in->points[(i+1)%in->numpoints];
2006
2007                 dot = dists[i] / (dists[i]-dists[i+1]);
2008                 for (j = 0;j < 3;j++)
2009                 {       // avoid round off error when possible
2010                         if (split->normal[j] == 1)
2011                                 mid[j] = split->dist;
2012                         else if (split->normal[j] == -1)
2013                                 mid[j] = -split->dist;
2014                         else
2015                                 mid[j] = p1[j] + dot* (p2[j]-p1[j]);
2016                 }
2017
2018                 VectorCopy(mid, neww->points[neww->numpoints]);
2019                 neww->numpoints++;
2020         }
2021
2022         // free the original winding
2023         FreeWinding(in);
2024
2025         return neww;
2026 }
2027
2028
2029 /*
2030 ==================
2031 DivideWinding
2032
2033 Divides a winding by a plane, producing one or two windings.  The
2034 original winding is not damaged or freed.  If only on one side, the
2035 returned winding will be the input winding.  If on both sides, two
2036 new windings will be created.
2037 ==================
2038 */
2039 static void DivideWinding(winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
2040 {
2041         double  dists[MAX_POINTS_ON_WINDING + 1];
2042         int             sides[MAX_POINTS_ON_WINDING + 1];
2043         int             counts[3];
2044         double  dot;
2045         int             i, j;
2046         double  *p1, *p2;
2047         double  mid[3];
2048         winding_t       *f, *b;
2049         int             maxpts;
2050
2051         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
2052
2053         // determine sides for each point
2054         for (i = 0;i < in->numpoints;i++)
2055         {
2056                 dot = DotProduct(in->points[i], split->normal);
2057                 dot -= split->dist;
2058                 dists[i] = dot;
2059                 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
2060                 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
2061                 else sides[i] = SIDE_ON;
2062                 counts[sides[i]]++;
2063         }
2064         sides[i] = sides[0];
2065         dists[i] = dists[0];
2066
2067         *front = *back = NULL;
2068
2069         if (!counts[0])
2070         {
2071                 *back = in;
2072                 return;
2073         }
2074         if (!counts[1])
2075         {
2076                 *front = in;
2077                 return;
2078         }
2079
2080         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
2081
2082         if (maxpts > MAX_POINTS_ON_WINDING)
2083                 Sys_Error("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
2084
2085         *front = f = NewWinding(maxpts);
2086         *back = b = NewWinding(maxpts);
2087
2088         for (i = 0;i < in->numpoints;i++)
2089         {
2090                 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
2091                         Sys_Error("DivideWinding: points exceeded estimate");
2092
2093                 p1 = in->points[i];
2094
2095                 if (sides[i] == SIDE_ON)
2096                 {
2097                         VectorCopy(p1, f->points[f->numpoints]);
2098                         f->numpoints++;
2099                         VectorCopy(p1, b->points[b->numpoints]);
2100                         b->numpoints++;
2101                         continue;
2102                 }
2103
2104                 if (sides[i] == SIDE_FRONT)
2105                 {
2106                         VectorCopy(p1, f->points[f->numpoints]);
2107                         f->numpoints++;
2108                 }
2109                 else if (sides[i] == SIDE_BACK)
2110                 {
2111                         VectorCopy(p1, b->points[b->numpoints]);
2112                         b->numpoints++;
2113                 }
2114
2115                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2116                         continue;
2117
2118                 // generate a split point
2119                 p2 = in->points[(i+1)%in->numpoints];
2120
2121                 dot = dists[i] / (dists[i]-dists[i+1]);
2122                 for (j = 0;j < 3;j++)
2123                 {       // avoid round off error when possible
2124                         if (split->normal[j] == 1)
2125                                 mid[j] = split->dist;
2126                         else if (split->normal[j] == -1)
2127                                 mid[j] = -split->dist;
2128                         else
2129                                 mid[j] = p1[j] + dot* (p2[j]-p1[j]);
2130                 }
2131
2132                 VectorCopy(mid, f->points[f->numpoints]);
2133                 f->numpoints++;
2134                 VectorCopy(mid, b->points[b->numpoints]);
2135                 b->numpoints++;
2136         }
2137 }
2138
2139 typedef struct portal_s
2140 {
2141         mplane_t plane;
2142         mnode_t *nodes[2];              // [0] = front side of plane
2143         struct portal_s *next[2];
2144         winding_t *winding;
2145         struct portal_s *chain; // all portals are linked into a list
2146 }
2147 portal_t;
2148
2149 static portal_t *portalchain;
2150
2151 /*
2152 ===========
2153 AllocPortal
2154 ===========
2155 */
2156 static portal_t *AllocPortal(void)
2157 {
2158         portal_t *p;
2159         p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2160         p->chain = portalchain;
2161         portalchain = p;
2162         return p;
2163 }
2164
2165 static void FreePortal(portal_t *p)
2166 {
2167         Mem_Free(p);
2168 }
2169
2170 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2171 {
2172         // calculate children first
2173         if (node->children[0]->contents >= 0)
2174                 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2175         if (node->children[1]->contents >= 0)
2176                 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2177
2178         // make combined bounding box from children
2179         node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2180         node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2181         node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2182         node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2183         node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2184         node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2185 }
2186
2187 static void Mod_Q1BSP_FinalizePortals(void)
2188 {
2189         int i, j, numportals, numpoints;
2190         portal_t *p, *pnext;
2191         mportal_t *portal;
2192         mvertex_t *point;
2193         mleaf_t *leaf, *endleaf;
2194         winding_t *w;
2195
2196         // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
2197         leaf = loadmodel->leafs;
2198         endleaf = leaf + loadmodel->numleafs;
2199         for (;leaf < endleaf;leaf++)
2200         {
2201                 VectorSet(leaf->mins,  2000000000,  2000000000,  2000000000);
2202                 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2203         }
2204         p = portalchain;
2205         while (p)
2206         {
2207                 if (p->winding)
2208                 {
2209                         for (i = 0;i < 2;i++)
2210                         {
2211                                 leaf = (mleaf_t *)p->nodes[i];
2212                                 w = p->winding;
2213                                 for (j = 0;j < w->numpoints;j++)
2214                                 {
2215                                         if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2216                                         if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2217                                         if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2218                                         if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2219                                         if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2220                                         if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2221                                 }
2222                         }
2223                 }
2224                 p = p->chain;
2225         }
2226
2227         Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->nodes);
2228
2229         // tally up portal and point counts
2230         p = portalchain;
2231         numportals = 0;
2232         numpoints = 0;
2233         while (p)
2234         {
2235                 // note: this check must match the one below or it will usually corrupt memory
2236                 // 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
2237                 if (p->winding && p->nodes[0] != p->nodes[1]
2238                  && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2239                  && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2240                 {
2241                         numportals += 2;
2242                         numpoints += p->winding->numpoints * 2;
2243                 }
2244                 p = p->chain;
2245         }
2246         loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2247         loadmodel->numportals = numportals;
2248         loadmodel->portalpoints = (void *)((qbyte *) loadmodel->portals + numportals * sizeof(mportal_t));
2249         loadmodel->numportalpoints = numpoints;
2250         // clear all leaf portal chains
2251         for (i = 0;i < loadmodel->numleafs;i++)
2252                 loadmodel->leafs[i].portals = NULL;
2253         // process all portals in the global portal chain, while freeing them
2254         portal = loadmodel->portals;
2255         point = loadmodel->portalpoints;
2256         p = portalchain;
2257         portalchain = NULL;
2258         while (p)
2259         {
2260                 pnext = p->chain;
2261
2262                 if (p->winding)
2263                 {
2264                         // note: this check must match the one above or it will usually corrupt memory
2265                         // 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
2266                         if (p->nodes[0] != p->nodes[1]
2267                          && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2268                          && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2269                         {
2270                                 // first make the back to front portal(forward portal)
2271                                 portal->points = point;
2272                                 portal->numpoints = p->winding->numpoints;
2273                                 portal->plane.dist = p->plane.dist;
2274                                 VectorCopy(p->plane.normal, portal->plane.normal);
2275                                 portal->here = (mleaf_t *)p->nodes[1];
2276                                 portal->past = (mleaf_t *)p->nodes[0];
2277                                 // copy points
2278                                 for (j = 0;j < portal->numpoints;j++)
2279                                 {
2280                                         VectorCopy(p->winding->points[j], point->position);
2281                                         point++;
2282                                 }
2283                                 PlaneClassify(&portal->plane);
2284
2285                                 // link into leaf's portal chain
2286                                 portal->next = portal->here->portals;
2287                                 portal->here->portals = portal;
2288
2289                                 // advance to next portal
2290                                 portal++;
2291
2292                                 // then make the front to back portal(backward portal)
2293                                 portal->points = point;
2294                                 portal->numpoints = p->winding->numpoints;
2295                                 portal->plane.dist = -p->plane.dist;
2296                                 VectorNegate(p->plane.normal, portal->plane.normal);
2297                                 portal->here = (mleaf_t *)p->nodes[0];
2298                                 portal->past = (mleaf_t *)p->nodes[1];
2299                                 // copy points
2300                                 for (j = portal->numpoints - 1;j >= 0;j--)
2301                                 {
2302                                         VectorCopy(p->winding->points[j], point->position);
2303                                         point++;
2304                                 }
2305                                 PlaneClassify(&portal->plane);
2306
2307                                 // link into leaf's portal chain
2308                                 portal->next = portal->here->portals;
2309                                 portal->here->portals = portal;
2310
2311                                 // advance to next portal
2312                                 portal++;
2313                         }
2314                         FreeWinding(p->winding);
2315                 }
2316                 FreePortal(p);
2317                 p = pnext;
2318         }
2319 }
2320
2321 /*
2322 =============
2323 AddPortalToNodes
2324 =============
2325 */
2326 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2327 {
2328         if (!front)
2329                 Host_Error("AddPortalToNodes: NULL front node");
2330         if (!back)
2331                 Host_Error("AddPortalToNodes: NULL back node");
2332         if (p->nodes[0] || p->nodes[1])
2333                 Host_Error("AddPortalToNodes: already included");
2334         // 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
2335
2336         p->nodes[0] = front;
2337         p->next[0] = (portal_t *)front->portals;
2338         front->portals = (mportal_t *)p;
2339
2340         p->nodes[1] = back;
2341         p->next[1] = (portal_t *)back->portals;
2342         back->portals = (mportal_t *)p;
2343 }
2344
2345 /*
2346 =============
2347 RemovePortalFromNode
2348 =============
2349 */
2350 static void RemovePortalFromNodes(portal_t *portal)
2351 {
2352         int i;
2353         mnode_t *node;
2354         void **portalpointer;
2355         portal_t *t;
2356         for (i = 0;i < 2;i++)
2357         {
2358                 node = portal->nodes[i];
2359
2360                 portalpointer = (void **) &node->portals;
2361                 while (1)
2362                 {
2363                         t = *portalpointer;
2364                         if (!t)
2365                                 Host_Error("RemovePortalFromNodes: portal not in leaf");
2366
2367                         if (t == portal)
2368                         {
2369                                 if (portal->nodes[0] == node)
2370                                 {
2371                                         *portalpointer = portal->next[0];
2372                                         portal->nodes[0] = NULL;
2373                                 }
2374                                 else if (portal->nodes[1] == node)
2375                                 {
2376                                         *portalpointer = portal->next[1];
2377                                         portal->nodes[1] = NULL;
2378                                 }
2379                                 else
2380                                         Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2381                                 break;
2382                         }
2383
2384                         if (t->nodes[0] == node)
2385                                 portalpointer = (void **) &t->next[0];
2386                         else if (t->nodes[1] == node)
2387                                 portalpointer = (void **) &t->next[1];
2388                         else
2389                                 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2390                 }
2391         }
2392 }
2393
2394 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2395 {
2396         int side;
2397         mnode_t *front, *back, *other_node;
2398         mplane_t clipplane, *plane;
2399         portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2400         winding_t *nodeportalwinding, *frontwinding, *backwinding;
2401
2402         // if a leaf, we're done
2403         if (node->contents)
2404                 return;
2405
2406         plane = node->plane;
2407
2408         front = node->children[0];
2409         back = node->children[1];
2410         if (front == back)
2411                 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2412
2413         // create the new portal by generating a polygon for the node plane,
2414         // and clipping it by all of the other portals(which came from nodes above this one)
2415         nodeportal = AllocPortal();
2416         nodeportal->plane = *node->plane;
2417
2418         nodeportalwinding = BaseWindingForPlane(node->plane);
2419         side = 0;       // shut up compiler warning
2420         for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2421         {
2422                 clipplane = portal->plane;
2423                 if (portal->nodes[0] == portal->nodes[1])
2424                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2425                 if (portal->nodes[0] == node)
2426                         side = 0;
2427                 else if (portal->nodes[1] == node)
2428                 {
2429                         clipplane.dist = -clipplane.dist;
2430                         VectorNegate(clipplane.normal, clipplane.normal);
2431                         side = 1;
2432                 }
2433                 else
2434                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2435
2436                 nodeportalwinding = ClipWinding(nodeportalwinding, &clipplane, true);
2437                 if (!nodeportalwinding)
2438                 {
2439                         Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2440                         break;
2441                 }
2442         }
2443
2444         if (nodeportalwinding)
2445         {
2446                 // if the plane was not clipped on all sides, there was an error
2447                 nodeportal->winding = nodeportalwinding;
2448                 AddPortalToNodes(nodeportal, front, back);
2449         }
2450
2451         // split the portals of this node along this node's plane and assign them to the children of this node
2452         // (migrating the portals downward through the tree)
2453         for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2454         {
2455                 if (portal->nodes[0] == portal->nodes[1])
2456                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2457                 if (portal->nodes[0] == node)
2458                         side = 0;
2459                 else if (portal->nodes[1] == node)
2460                         side = 1;
2461                 else
2462                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2463                 nextportal = portal->next[side];
2464
2465                 other_node = portal->nodes[!side];
2466                 RemovePortalFromNodes(portal);
2467
2468                 // cut the portal into two portals, one on each side of the node plane
2469                 DivideWinding(portal->winding, plane, &frontwinding, &backwinding);
2470
2471                 if (!frontwinding)
2472                 {
2473                         if (side == 0)
2474                                 AddPortalToNodes(portal, back, other_node);
2475                         else
2476                                 AddPortalToNodes(portal, other_node, back);
2477                         continue;
2478                 }
2479                 if (!backwinding)
2480                 {
2481                         if (side == 0)
2482                                 AddPortalToNodes(portal, front, other_node);
2483                         else
2484                                 AddPortalToNodes(portal, other_node, front);
2485                         continue;
2486                 }
2487
2488                 // the winding is split
2489                 splitportal = AllocPortal();
2490                 temp = splitportal->chain;
2491                 *splitportal = *portal;
2492                 splitportal->chain = temp;
2493                 splitportal->winding = backwinding;
2494                 FreeWinding(portal->winding);
2495                 portal->winding = frontwinding;
2496
2497                 if (side == 0)
2498                 {
2499                         AddPortalToNodes(portal, front, other_node);
2500                         AddPortalToNodes(splitportal, back, other_node);
2501                 }
2502                 else
2503                 {
2504                         AddPortalToNodes(portal, other_node, front);
2505                         AddPortalToNodes(splitportal, other_node, back);
2506                 }
2507         }
2508
2509         Mod_Q1BSP_RecursiveNodePortals(front);
2510         Mod_Q1BSP_RecursiveNodePortals(back);
2511 }
2512
2513
2514 static void Mod_Q1BSP_MakePortals(void)
2515 {
2516         portalchain = NULL;
2517         Mod_Q1BSP_RecursiveNodePortals(loadmodel->nodes);
2518         Mod_Q1BSP_FinalizePortals();
2519 }
2520
2521 static void Mod_Q1BSP_BuildSurfaceNeighbors(msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2522 {
2523 #if 0
2524         int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
2525         msurface_t *surf, *s;
2526         float *v0, *v1, *v2, *v3;
2527         for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2528                 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2529         for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2530         {
2531                 for (vertnum = surf->poly_numverts - 1, vertnum2 = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;vertnum2 < surf->poly_numverts;vertnum = vertnum2, vertnum2++, v0 = v1, v1 += 3)
2532                 {
2533                         if (surf->neighborsurfaces[vertnum])
2534                                 continue;
2535                         surf->neighborsurfaces[vertnum] = NULL;
2536                         for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2537                         {
2538                                 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2539                                  || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2540                                  || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2541                                  || s == surf)
2542                                         continue;
2543                                 for (vnum = 0;vnum < s->poly_numverts;vnum++)
2544                                         if (s->neighborsurfaces[vnum] == surf)
2545                                                 break;
2546                                 if (vnum < s->poly_numverts)
2547                                         continue;
2548                                 for (vnum = s->poly_numverts - 1, vnum2 = 0, v2 = s->poly_verts + (s->poly_numverts - 1) * 3, v3 = s->poly_verts;vnum2 < s->poly_numverts;vnum = vnum2, vnum2++, v2 = v3, v3 += 3)
2549                                 {
2550                                         if (s->neighborsurfaces[vnum] == NULL
2551                                          && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2552                                           || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
2553                                         {
2554                                                 surf->neighborsurfaces[vertnum] = s;
2555                                                 s->neighborsurfaces[vnum] = surf;
2556                                                 break;
2557                                         }
2558                                 }
2559                                 if (vnum < s->poly_numverts)
2560                                         break;
2561                         }
2562                 }
2563         }
2564 #endif
2565 }
2566
2567 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2568 {
2569         int i, j, stylecounts[256], totalcount, remapstyles[256];
2570         msurface_t *surf;
2571         memset(stylecounts, 0, sizeof(stylecounts));
2572         for (i = 0;i < model->nummodelsurfaces;i++)
2573         {
2574                 surf = model->surfaces + model->firstmodelsurface + i;
2575                 for (j = 0;j < MAXLIGHTMAPS;j++)
2576                         stylecounts[surf->styles[j]]++;
2577         }
2578         totalcount = 0;
2579         model->light_styles = 0;
2580         for (i = 0;i < 255;i++)
2581         {
2582                 if (stylecounts[i])
2583                 {
2584                         remapstyles[i] = model->light_styles++;
2585                         totalcount += stylecounts[i] + 1;
2586                 }
2587         }
2588         if (!totalcount)
2589                 return;
2590         model->light_style = Mem_Alloc(mempool, model->light_styles * sizeof(qbyte));
2591         model->light_stylevalue = Mem_Alloc(mempool, model->light_styles * sizeof(int));
2592         model->light_styleupdatechains = Mem_Alloc(mempool, model->light_styles * sizeof(msurface_t **));
2593         model->light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2594         model->light_styles = 0;
2595         for (i = 0;i < 255;i++)
2596                 if (stylecounts[i])
2597                         model->light_style[model->light_styles++] = i;
2598         j = 0;
2599         for (i = 0;i < model->light_styles;i++)
2600         {
2601                 model->light_styleupdatechains[i] = model->light_styleupdatechainsbuffer + j;
2602                 j += stylecounts[model->light_style[i]] + 1;
2603         }
2604         for (i = 0;i < model->nummodelsurfaces;i++)
2605         {
2606                 surf = model->surfaces + model->firstmodelsurface + i;
2607                 for (j = 0;j < MAXLIGHTMAPS;j++)
2608                         if (surf->styles[j] != 255)
2609                                 *model->light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
2610         }
2611         j = 0;
2612         for (i = 0;i < model->light_styles;i++)
2613         {
2614                 *model->light_styleupdatechains[i] = NULL;
2615                 model->light_styleupdatechains[i] = model->light_styleupdatechainsbuffer + j;
2616                 j += stylecounts[model->light_style[i]] + 1;
2617         }
2618 }
2619
2620 static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model)
2621 {
2622         int i, j;
2623         for (i = 0;i < model->numtextures;i++)
2624                 model->pvstexturechainslength[i] = 0;
2625         for (i = 0, j = model->firstmodelsurface;i < model->nummodelsurfaces;i++, j++)
2626         {
2627                 if (model->surfacepvsframes[j] == model->pvsframecount)
2628                 {
2629                         model->pvssurflist[model->pvssurflistlength++] = j;
2630                         model->pvstexturechainslength[model->surfaces[j].texinfo->texture->number]++;
2631                 }
2632         }
2633         for (i = 0, j = 0;i < model->numtextures;i++)
2634         {
2635                 if (model->pvstexturechainslength[i])
2636                 {
2637                         model->pvstexturechains[i] = model->pvstexturechainsbuffer + j;
2638                         j += model->pvstexturechainslength[i] + 1;
2639                 }
2640                 else
2641                         model->pvstexturechains[i] = NULL;
2642         }
2643         for (i = 0, j = model->firstmodelsurface;i < model->nummodelsurfaces;i++, j++)
2644                 if (model->surfacepvsframes[j] == model->pvsframecount)
2645                         *model->pvstexturechains[model->surfaces[j].texinfo->texture->number]++ = model->surfaces + j;
2646         for (i = 0;i < model->numtextures;i++)
2647         {
2648                 if (model->pvstexturechainslength[i])
2649                 {
2650                         *model->pvstexturechains[i] = NULL;
2651                         model->pvstexturechains[i] -= model->pvstexturechainslength[i];
2652                 }
2653         }
2654 }
2655
2656 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
2657 extern void R_Model_Brush_Draw(entity_render_t *ent);
2658 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
2659 extern void R_Model_Brush_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz);
2660 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
2661 {
2662         int i, j, k;
2663         dheader_t *header;
2664         dmodel_t *bm;
2665         mempool_t *mainmempool;
2666         char *loadname;
2667         model_t *originalloadmodel;
2668         float dist, modelyawradius, modelradius, *vec;
2669         msurface_t *surf;
2670         surfmesh_t *mesh;
2671
2672         mod->type = mod_brush;
2673
2674         header = (dheader_t *)buffer;
2675
2676         i = LittleLong(header->version);
2677         if (i != BSPVERSION && i != 30)
2678                 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
2679         mod->ishlbsp = i == 30;
2680
2681         mod->FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
2682         mod->PointInLeaf = Mod_Q1BSP_PointInLeaf;
2683         mod->PointContents = Mod_Q1BSP_PointContents;
2684         mod->LeafPVS = Mod_Q1BSP_LeafPVS;
2685         mod->BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
2686
2687         if (loadmodel->isworldmodel)
2688         {
2689                 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2690                 // until we get a texture for it...
2691                 R_ResetQuakeSky();
2692         }
2693
2694 // swap all the lumps
2695         mod_base = (qbyte *)header;
2696
2697         for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
2698                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
2699
2700 // load into heap
2701
2702         // store which lightmap format to use
2703         mod->lightmaprgba = r_lightmaprgba.integer;
2704
2705         Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
2706         Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
2707         Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
2708         Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
2709         Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
2710         Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
2711         Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
2712         Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
2713         Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
2714         Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
2715         Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
2716         Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
2717         Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
2718         Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
2719         Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
2720
2721         Mod_Q1BSP_MakeHull0();
2722         Mod_Q1BSP_MakePortals();
2723
2724         mod->numframes = 2;             // regular and alternate animation
2725
2726         mainmempool = mod->mempool;
2727         loadname = mod->name;
2728
2729         Mod_Q1BSP_LoadLightList();
2730         originalloadmodel = loadmodel;
2731
2732 //
2733 // set up the submodels(FIXME: this is confusing)
2734 //
2735         for (i = 0;i < mod->numsubmodels;i++)
2736         {
2737                 bm = &mod->submodels[i];
2738
2739                 mod->hulls[0].firstclipnode = bm->headnode[0];
2740                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2741                 {
2742                         mod->hulls[j].firstclipnode = bm->headnode[j];
2743                         mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2744                 }
2745
2746                 mod->firstmodelsurface = bm->firstface;
2747                 mod->nummodelsurfaces = bm->numfaces;
2748
2749                 // this gets altered below if sky is used
2750                 mod->DrawSky = NULL;
2751                 mod->Draw = R_Model_Brush_Draw;
2752                 mod->DrawFakeShadow = NULL;
2753                 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
2754                 mod->DrawLight = R_Model_Brush_DrawLight;
2755                 mod->pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->numtextures * sizeof(msurface_t **));
2756                 mod->pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->nummodelsurfaces + mod->numtextures) * sizeof(msurface_t *));
2757                 mod->pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->numtextures * sizeof(int));
2758                 Mod_Q1BSP_BuildPVSTextureChains(mod);
2759                 Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod);
2760                 if (mod->nummodelsurfaces)
2761                 {
2762                         // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2763                         mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2764                         mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2765                         modelyawradius = 0;
2766                         modelradius = 0;
2767                         for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2768                         {
2769                                 // we only need to have a drawsky function if it is used(usually only on world model)
2770                                 if (surf->texinfo->texture->shader == &Cshader_sky)
2771                                         mod->DrawSky = R_Model_Brush_DrawSky;
2772                                 // LordHavoc: submodels always clip, even if water
2773                                 if (mod->numsubmodels - 1)
2774                                         surf->flags |= SURF_SOLIDCLIP;
2775                                 // calculate bounding shapes
2776                                 for (mesh = surf->mesh;mesh;mesh = mesh->chain)
2777                                 {
2778                                         for (k = 0, vec = mesh->vertex3f;k < mesh->numverts;k++, vec += 3)
2779                                         {
2780                                                 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2781                                                 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2782                                                 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2783                                                 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2784                                                 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2785                                                 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2786                                                 dist = vec[0]*vec[0]+vec[1]*vec[1];
2787                                                 if (modelyawradius < dist)
2788                                                         modelyawradius = dist;
2789                                                 dist += vec[2]*vec[2];
2790                                                 if (modelradius < dist)
2791                                                         modelradius = dist;
2792                                         }
2793                                 }
2794                         }
2795                         modelyawradius = sqrt(modelyawradius);
2796                         modelradius = sqrt(modelradius);
2797                         mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2798                         mod->yawmins[2] = mod->normalmins[2];
2799                         mod->yawmaxs[2] = mod->normalmaxs[2];
2800                         mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2801                         mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2802                         mod->radius = modelradius;
2803                         mod->radius2 = modelradius * modelradius;
2804                 }
2805                 else
2806                 {
2807                         // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
2808                         Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2809                 }
2810                 Mod_Q1BSP_BuildSurfaceNeighbors(mod->surfaces + mod->firstmodelsurface, mod->nummodelsurfaces, originalloadmodel->mempool);
2811
2812                 mod->numleafs = bm->visleafs;
2813
2814                 // LordHavoc: only register submodels if it is the world
2815                 // (prevents bsp models from replacing world submodels)
2816                 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2817                 {
2818                         char    name[10];
2819                         // duplicate the basic information
2820                         sprintf(name, "*%i", i+1);
2821                         loadmodel = Mod_FindName(name);
2822                         *loadmodel = *mod;
2823                         strcpy(loadmodel->name, name);
2824                         // textures and memory belong to the main model
2825                         loadmodel->texturepool = NULL;
2826                         loadmodel->mempool = NULL;
2827                         mod = loadmodel;
2828                 }
2829         }
2830
2831         loadmodel = originalloadmodel;
2832         //Mod_Q1BSP_ProcessLightList();
2833 }
2834
2835 static void Mod_Q2BSP_LoadEntities(lump_t *l)
2836 {
2837 }
2838
2839 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
2840 {
2841 /*
2842         d_t *in;
2843         m_t *out;
2844         int i, count;
2845
2846         in = (void *)(mod_base + l->fileofs);
2847         if (l->filelen % sizeof(*in))
2848                 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
2849         count = l->filelen / sizeof(*in);
2850         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2851
2852         loadmodel-> = out;
2853         loadmodel->num = count;
2854
2855         for (i = 0;i < count;i++, in++, out++)
2856         {
2857         }
2858 */
2859 }
2860
2861 static void Mod_Q2BSP_LoadVertices(lump_t *l)
2862 {
2863 /*
2864         d_t *in;
2865         m_t *out;
2866         int i, count;
2867
2868         in = (void *)(mod_base + l->fileofs);
2869         if (l->filelen % sizeof(*in))
2870                 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
2871         count = l->filelen / sizeof(*in);
2872         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2873
2874         loadmodel-> = out;
2875         loadmodel->num = count;
2876
2877         for (i = 0;i < count;i++, in++, out++)
2878         {
2879         }
2880 */
2881 }
2882
2883 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
2884 {
2885 /*
2886         d_t *in;
2887         m_t *out;
2888         int i, count;
2889
2890         in = (void *)(mod_base + l->fileofs);
2891         if (l->filelen % sizeof(*in))
2892                 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
2893         count = l->filelen / sizeof(*in);
2894         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2895
2896         loadmodel-> = out;
2897         loadmodel->num = count;
2898
2899         for (i = 0;i < count;i++, in++, out++)
2900         {
2901         }
2902 */
2903 }
2904
2905 static void Mod_Q2BSP_LoadNodes(lump_t *l)
2906 {
2907 /*
2908         d_t *in;
2909         m_t *out;
2910         int i, count;
2911
2912         in = (void *)(mod_base + l->fileofs);
2913         if (l->filelen % sizeof(*in))
2914                 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
2915         count = l->filelen / sizeof(*in);
2916         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2917
2918         loadmodel-> = out;
2919         loadmodel->num = count;
2920
2921         for (i = 0;i < count;i++, in++, out++)
2922         {
2923         }
2924 */
2925 }
2926
2927 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
2928 {
2929 /*
2930         d_t *in;
2931         m_t *out;
2932         int i, count;
2933
2934         in = (void *)(mod_base + l->fileofs);
2935         if (l->filelen % sizeof(*in))
2936                 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
2937         count = l->filelen / sizeof(*in);
2938         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2939
2940         loadmodel-> = out;
2941         loadmodel->num = count;
2942
2943         for (i = 0;i < count;i++, in++, out++)
2944         {
2945         }
2946 */
2947 }
2948
2949 static void Mod_Q2BSP_LoadFaces(lump_t *l)
2950 {
2951 /*
2952         d_t *in;
2953         m_t *out;
2954         int i, count;
2955
2956         in = (void *)(mod_base + l->fileofs);
2957         if (l->filelen % sizeof(*in))
2958                 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
2959         count = l->filelen / sizeof(*in);
2960         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2961
2962         loadmodel-> = out;
2963         loadmodel->num = count;
2964
2965         for (i = 0;i < count;i++, in++, out++)
2966         {
2967         }
2968 */
2969 }
2970
2971 static void Mod_Q2BSP_LoadLighting(lump_t *l)
2972 {
2973 /*
2974         d_t *in;
2975         m_t *out;
2976         int i, count;
2977
2978         in = (void *)(mod_base + l->fileofs);
2979         if (l->filelen % sizeof(*in))
2980                 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
2981         count = l->filelen / sizeof(*in);
2982         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2983
2984         loadmodel-> = out;
2985         loadmodel->num = count;
2986
2987         for (i = 0;i < count;i++, in++, out++)
2988         {
2989         }
2990 */
2991 }
2992
2993 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
2994 {
2995 /*
2996         d_t *in;
2997         m_t *out;
2998         int i, count;
2999
3000         in = (void *)(mod_base + l->fileofs);
3001         if (l->filelen % sizeof(*in))
3002                 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3003         count = l->filelen / sizeof(*in);
3004         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3005
3006         loadmodel-> = out;
3007         loadmodel->num = count;
3008
3009         for (i = 0;i < count;i++, in++, out++)
3010         {
3011         }
3012 */
3013 }
3014
3015 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3016 {
3017 /*
3018         d_t *in;
3019         m_t *out;
3020         int i, count;
3021
3022         in = (void *)(mod_base + l->fileofs);
3023         if (l->filelen % sizeof(*in))
3024                 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3025         count = l->filelen / sizeof(*in);
3026         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3027
3028         loadmodel-> = out;
3029         loadmodel->num = count;
3030
3031         for (i = 0;i < count;i++, in++, out++)
3032         {
3033         }
3034 */
3035 }
3036
3037 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3038 {
3039 /*
3040         d_t *in;
3041         m_t *out;
3042         int i, count;
3043
3044         in = (void *)(mod_base + l->fileofs);
3045         if (l->filelen % sizeof(*in))
3046                 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3047         count = l->filelen / sizeof(*in);
3048         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3049
3050         loadmodel-> = out;
3051         loadmodel->num = count;
3052
3053         for (i = 0;i < count;i++, in++, out++)
3054         {
3055         }
3056 */
3057 }
3058
3059 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3060 {
3061 /*
3062         d_t *in;
3063         m_t *out;
3064         int i, count;
3065
3066         in = (void *)(mod_base + l->fileofs);
3067         if (l->filelen % sizeof(*in))
3068                 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3069         count = l->filelen / sizeof(*in);
3070         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3071
3072         loadmodel-> = out;
3073         loadmodel->num = count;
3074
3075         for (i = 0;i < count;i++, in++, out++)
3076         {
3077         }
3078 */
3079 }
3080
3081 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3082 {
3083 /*
3084         d_t *in;
3085         m_t *out;
3086         int i, count;
3087
3088         in = (void *)(mod_base + l->fileofs);
3089         if (l->filelen % sizeof(*in))
3090                 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3091         count = l->filelen / sizeof(*in);
3092         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3093
3094         loadmodel-> = out;
3095         loadmodel->num = count;
3096
3097         for (i = 0;i < count;i++, in++, out++)
3098         {
3099         }
3100 */
3101 }
3102
3103 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3104 {
3105 /*
3106         d_t *in;
3107         m_t *out;
3108         int i, count;
3109
3110         in = (void *)(mod_base + l->fileofs);
3111         if (l->filelen % sizeof(*in))
3112                 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3113         count = l->filelen / sizeof(*in);
3114         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3115
3116         loadmodel-> = out;
3117         loadmodel->num = count;
3118
3119         for (i = 0;i < count;i++, in++, out++)
3120         {
3121         }
3122 */
3123 }
3124
3125 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3126 {
3127 /*
3128         d_t *in;
3129         m_t *out;
3130         int i, count;
3131
3132         in = (void *)(mod_base + l->fileofs);
3133         if (l->filelen % sizeof(*in))
3134                 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3135         count = l->filelen / sizeof(*in);
3136         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3137
3138         loadmodel-> = out;
3139         loadmodel->num = count;
3140
3141         for (i = 0;i < count;i++, in++, out++)
3142         {
3143         }
3144 */
3145 }
3146
3147 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3148 {
3149 /*
3150         d_t *in;
3151         m_t *out;
3152         int i, count;
3153
3154         in = (void *)(mod_base + l->fileofs);
3155         if (l->filelen % sizeof(*in))
3156                 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3157         count = l->filelen / sizeof(*in);
3158         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3159
3160         loadmodel-> = out;
3161         loadmodel->num = count;
3162
3163         for (i = 0;i < count;i++, in++, out++)
3164         {
3165         }
3166 */
3167 }
3168
3169 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3170 {
3171 /*
3172         d_t *in;
3173         m_t *out;
3174         int i, count;
3175
3176         in = (void *)(mod_base + l->fileofs);
3177         if (l->filelen % sizeof(*in))
3178                 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3179         count = l->filelen / sizeof(*in);
3180         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3181
3182         loadmodel-> = out;
3183         loadmodel->num = count;
3184
3185         for (i = 0;i < count;i++, in++, out++)
3186         {
3187         }
3188 */
3189 }
3190
3191 static void Mod_Q2BSP_LoadModels(lump_t *l)
3192 {
3193 /*
3194         d_t *in;
3195         m_t *out;
3196         int i, count;
3197
3198         in = (void *)(mod_base + l->fileofs);
3199         if (l->filelen % sizeof(*in))
3200                 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3201         count = l->filelen / sizeof(*in);
3202         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3203
3204         loadmodel-> = out;
3205         loadmodel->num = count;
3206
3207         for (i = 0;i < count;i++, in++, out++)
3208         {
3209         }
3210 */
3211 }
3212
3213 void Mod_Q2BSP_Load(model_t *mod, void *buffer)
3214 {
3215         int i;
3216         q2dheader_t *header;
3217
3218         mod->type = mod_brushq2;
3219
3220         header = (q2dheader_t *)buffer;
3221
3222         i = LittleLong(header->version);
3223         if (i != Q2BSPVERSION)
3224                 Host_Error("Mod_Q2BSP_Load: %s has wrong version number(%i should be %i)", mod->name, i, BSPVERSION);
3225         mod->ishlbsp = false;
3226         if (loadmodel->isworldmodel)
3227         {
3228                 Cvar_SetValue("halflifebsp", mod->ishlbsp);
3229                 // until we get a texture for it...
3230                 R_ResetQuakeSky();
3231         }
3232
3233         mod_base = (qbyte *)header;
3234
3235         // swap all the lumps
3236         for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
3237                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3238
3239         // store which lightmap format to use
3240         mod->lightmaprgba = r_lightmaprgba.integer;
3241
3242         Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3243         Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3244         Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3245         Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3246         Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3247         Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3248         Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3249         Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3250         Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3251         Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3252         Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3253         Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3254         Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3255         Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3256         Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3257         Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3258         Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3259         // LordHavoc: must go last because this makes the submodels
3260         Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3261 }
3262
3263 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
3264 {
3265         Host_Error("Mod_Q3BSP_Load: not yet implemented\n");
3266 }
3267
3268 void Mod_IBSP_Load(model_t *mod, void *buffer)
3269 {
3270         int i = LittleLong(* ((int *)buffer));
3271         if (i == 46)
3272                 Mod_Q3BSP_Load(mod,buffer);
3273         else if (i == 38)
3274                 Mod_Q2BSP_Load(mod,buffer);
3275         else
3276                 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
3277 }
3278
3279 void Mod_MAP_Load(model_t *mod, void *buffer)
3280 {
3281         Host_Error("Mod_MAP_Load: not yet implemented\n");
3282 }
3283