got rid of buildnumber.c and buildnum program, now uses builddate.c (touched each...
[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
23 byte mod_novis[(MAX_MAP_LEAFS + 7)/ 8];
24
25 cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
26 cvar_t halflifebsp = {0, "halflifebsp", "0"};
27 cvar_t r_novis = {0, "r_novis", "0"};
28 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
29 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
30 cvar_t r_vertexsurfacesthreshold = {CVAR_SAVE, "r_vertexsurfacesthreshold", "0"};
31
32 /*
33 ===============
34 Mod_BrushInit
35 ===============
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_vertexsurfacesthreshold);
45         memset(mod_novis, 0xff, sizeof(mod_novis));
46 }
47
48 void Mod_Brush_SERAddEntity(void)
49 {
50         R_Clip_AddBox(currentrenderentity->mins, currentrenderentity->maxs, R_Entity_Callback, currentrenderentity, NULL);
51 }
52
53 /*
54 ===============
55 Mod_PointInLeaf
56 ===============
57 */
58 mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model)
59 {
60         mnode_t         *node;
61
62         Mod_CheckLoaded(model);
63 //      if (!model || !model->nodes)
64 //              Sys_Error ("Mod_PointInLeaf: bad model");
65
66         // LordHavoc: modified to start at first clip node,
67         // in other words: first node of the (sub)model
68         node = model->nodes + model->hulls[0].firstclipnode;
69         while (node->contents == 0)
70                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct (p,node->plane->normal)) < node->plane->dist];
71
72         return (mleaf_t *)node;
73 }
74
75 void Mod_FindNonSolidLocation(vec3_t pos, model_t *mod)
76 {
77         if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
78         pos[0]-=1;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
79         pos[0]+=2;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
80         pos[0]-=1;
81         pos[1]-=1;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
82         pos[1]+=2;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
83         pos[1]-=1;
84         pos[2]-=1;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
85         pos[2]+=2;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
86         pos[2]-=1;
87 }
88
89 /*
90 mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model)
91 {
92         mnode_t         *node;
93         float           d;
94         mplane_t        *plane;
95
96         if (!model || !model->nodes)
97                 Sys_Error ("Mod_PointInLeaf: bad model");
98
99         node = model->nodes;
100         while (1)
101         {
102                 if (node->contents < 0)
103                         return (mleaf_t *)node;
104                 plane = node->plane;
105                 d = DotProduct (p,plane->normal) - plane->dist;
106                 if (d > 0)
107                         node = node->children[0];
108                 else
109                         node = node->children[1];
110         }
111
112         return NULL;    // never reached
113 }
114 */
115
116 /*
117 ===================
118 Mod_DecompressVis
119 ===================
120 */
121 static byte *Mod_DecompressVis (byte *in, model_t *model)
122 {
123         static byte     decompressed[MAX_MAP_LEAFS/8];
124         int             c;
125         byte    *out;
126         int             row;
127
128         row = (model->numleafs+7)>>3;
129         out = decompressed;
130
131         /*
132         if (!in)
133         {       // no vis info, so make all visible
134                 while (row)
135                 {
136                         *out++ = 0xff;
137                         row--;
138                 }
139                 return decompressed;
140         }
141         */
142
143         do
144         {
145                 if (*in)
146                 {
147                         *out++ = *in++;
148                         continue;
149                 }
150
151                 c = in[1];
152                 in += 2;
153                 while (c)
154                 {
155                         *out++ = 0;
156                         c--;
157                 }
158         } while (out - decompressed < row);
159
160         return decompressed;
161 }
162
163 byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model)
164 {
165         if (r_novis.integer || leaf == model->leafs || leaf->compressed_vis == NULL)
166                 return mod_novis;
167         return Mod_DecompressVis (leaf->compressed_vis, model);
168 }
169
170 void Mod_SetupNoTexture(void)
171 {
172         int             x, y;
173         byte    pix[16][16][4];
174
175         for (y = 0;y < 16;y++)
176         {
177                 for (x = 0;x < 16;x++)
178                 {
179                         if ((y < 8) ^ (x < 8))
180                         {
181                                 pix[y][x][0] = 128;
182                                 pix[y][x][1] = 128;
183                                 pix[y][x][2] = 128;
184                                 pix[y][x][3] = 255;
185                         }
186                         else
187                         {
188                                 pix[y][x][0] = 64;
189                                 pix[y][x][1] = 64;
190                                 pix[y][x][2] = 64;
191                                 pix[y][x][3] = 255;
192                         }
193                 }
194         }
195
196         memset(&loadmodel->notexture, 0, sizeof(texture_t));
197         strcpy(loadmodel->notexture.name, "notexture");
198         loadmodel->notexture.width = 16;
199         loadmodel->notexture.height = 16;
200         loadmodel->notexture.flags = 0;
201         loadmodel->notexture.texture = R_LoadTexture(loadmodel->texturepool, "notexture", 16, 16, &pix[0][0][0], TEXTYPE_RGBA, TEXF_MIPMAP);
202 }
203
204 /*
205 =================
206 Mod_LoadTextures
207 =================
208 */
209 static void Mod_LoadTextures (lump_t *l)
210 {
211         int                             i, j, k, num, max, altmax, mtwidth, mtheight, *dofs;
212         miptex_t                *dmiptex;
213         texture_t               *tx, *tx2, *anims[10], *altanims[10];
214         dmiptexlump_t   *m;
215         byte                    *data, *mtdata, *data2;
216         char                    name[256];
217
218         Mod_SetupNoTexture();
219
220         if (!l->filelen)
221         {
222                 loadmodel->textures = NULL;
223                 return;
224         }
225
226         m = (dmiptexlump_t *)(mod_base + l->fileofs);
227
228         m->nummiptex = LittleLong (m->nummiptex);
229
230         loadmodel->numtextures = m->nummiptex;
231         loadmodel->textures = Mem_Alloc(loadmodel->mempool, m->nummiptex * sizeof(*loadmodel->textures));
232
233         // just to work around bounds checking when debugging with it (array index out of bounds error thing)
234         dofs = m->dataofs;
235         for (i = 0;i < m->nummiptex;i++)
236         {
237                 dofs[i] = LittleLong(dofs[i]);
238                 if (dofs[i] == -1)
239                         continue;
240                 dmiptex = (miptex_t *)((byte *)m + dofs[i]);
241                 mtwidth = LittleLong (dmiptex->width);
242                 mtheight = LittleLong (dmiptex->height);
243                 mtdata = NULL;
244                 j = LittleLong (dmiptex->offsets[0]);
245                 if (j)
246                 {
247                         // texture included
248                         if (j < 40 || j + mtwidth * mtheight > l->filelen)
249                                 Host_Error ("Texture %s is corrupt or incomplete\n", dmiptex->name);
250                         mtdata = (byte *)dmiptex + j;
251                 }
252
253                 if ((mtwidth & 15) || (mtheight & 15))
254                         Host_Error ("Texture %s is not 16 aligned", dmiptex->name);
255                 // LordHavoc: rewriting the map texture loader for GLQuake
256                 tx = Mem_Alloc(loadmodel->mempool, sizeof(texture_t));
257                 memset(tx, 0, sizeof(texture_t));
258                 tx->anim_total = 0;
259                 tx->alternate_anims = NULL;
260                 loadmodel->textures[i] = tx;
261
262                 // LordHavoc: force all names to lowercase and make sure they are terminated while copying
263                 for (j = 0;dmiptex->name[j] && j < 15;j++)
264                 {
265                         if (dmiptex->name[j] >= 'A' && dmiptex->name[j] <= 'Z')
266                                 tx->name[j] = dmiptex->name[j] + ('a' - 'A');
267                         else
268                                 tx->name[j] = dmiptex->name[j];
269                 }
270                 for (;j < 16;j++)
271                         tx->name[j] = 0;
272
273                 if (!tx->name[0])
274                 {
275                         Con_Printf("warning: unnamed texture in %s\n", loadmodel->name);
276                         sprintf(tx->name, "unnamed%i", i);
277                 }
278
279                 tx->width = mtwidth;
280                 tx->height = mtheight;
281                 tx->texture = NULL;
282                 tx->glowtexture = NULL;
283                 tx->fogtexture = NULL;
284
285                 if (!loadmodel->ishlbsp && !strncmp(tx->name,"sky",3) && mtwidth == 256 && mtheight == 128) // LordHavoc: HL sky textures are entirely unrelated
286                 {
287                         data = loadimagepixels(tx->name, false, 0, 0);
288                         if (data)
289                         {
290                                 if (image_width == 256 && image_height == 128)
291                                 {
292                                         if (loadmodel->isworldmodel)
293                                                 R_InitSky (data, 4);
294                                         Mem_Free(data);
295                                 }
296                                 else
297                                 {
298                                         Mem_Free(data);
299                                         Host_Error("Mod_LoadTextures: replacement sky image must be 256x128 pixels\n");
300                                 }
301                         }
302                         else if (loadmodel->isworldmodel)
303                                 R_InitSky (mtdata, 1);
304                 }
305                 else if ((tx->texture = loadtextureimagewithmask(loadmodel->texturepool, tx->name, 0, 0, false, true, true)))
306                 {
307                         tx->fogtexture = image_masktex;
308                         strcpy(name, tx->name);
309                         strcat(name, "_glow");
310                         tx->glowtexture = loadtextureimage(loadmodel->texturepool, name, 0, 0, false, true, true);
311                 }
312                 else
313                 {
314                         if (loadmodel->ishlbsp)
315                         {
316                                 if (mtdata && (data = W_ConvertWAD3Texture(dmiptex)))
317                                 {
318                                         // texture included
319                                         tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
320                                         if (R_TextureHasAlpha(tx->texture))
321                                         {
322                                                 // make mask texture
323                                                 for (j = 0;j < image_width * image_height;j++)
324                                                         data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
325                                                 strcpy(name, tx->name);
326                                                 strcat(name, "_fog");
327                                                 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
328                                         }
329                                         Mem_Free(data);
330                                 }
331                                 else if ((data = W_GetTexture(tx->name)))
332                                 {
333                                         // get the size from the wad texture
334                                         tx->width = image_width;
335                                         tx->height = image_height;
336                                         tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
337                                         if (R_TextureHasAlpha(tx->texture))
338                                         {
339                                                 // make mask texture
340                                                 for (j = 0;j < image_width * image_height;j++)
341                                                         data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
342                                                 strcpy(name, tx->name);
343                                                 strcat(name, "_fog");
344                                                 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
345                                         }
346                                         Mem_Free(data);
347                                 }
348                                 else
349                                 {
350                                         tx->width = 16;
351                                         tx->height = 16;
352                                         tx->texture = loadmodel->notexture.texture;
353                                 }
354                         }
355                         else
356                         {
357                                 if (mtdata) // texture included
358                                 {
359                                         int fullbrights;
360                                         data = mtdata;
361                                         fullbrights = false;
362                                         if (r_fullbrights.value && tx->name[0] != '*')
363                                         {
364                                                 for (j = 0;j < tx->width*tx->height;j++)
365                                                 {
366                                                         if (data[j] >= 224) // fullbright
367                                                         {
368                                                                 fullbrights = true;
369                                                                 break;
370                                                         }
371                                                 }
372                                         }
373                                         if (fullbrights)
374                                         {
375                                                 data2 = Mem_Alloc(loadmodel->mempool, tx->width*tx->height);
376                                                 for (j = 0;j < tx->width*tx->height;j++)
377                                                         data2[j] = data[j] >= 224 ? 0 : data[j]; // no fullbrights
378                                                 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
379                                                 strcpy(name, tx->name);
380                                                 strcat(name, "_glow");
381                                                 for (j = 0;j < tx->width*tx->height;j++)
382                                                         data2[j] = data[j] >= 224 ? data[j] : 0; // only fullbrights
383                                                 tx->glowtexture = R_LoadTexture (loadmodel->texturepool, name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
384                                                 Mem_Free(data2);
385                                         }
386                                         else
387                                                 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
388                                 }
389                                 else // no texture, and no external replacement texture was found
390                                 {
391                                         tx->width = 16;
392                                         tx->height = 16;
393                                         tx->texture = loadmodel->notexture.texture;
394                                 }
395                         }
396                 }
397
398                 if (tx->name[0] == '*')
399                 {
400                         tx->flags |= (SURF_DRAWTURB | SURF_LIGHTBOTHSIDES);
401                         // LordHavoc: some turbulent textures should be fullbright and solid
402                         if (!strncmp(tx->name,"*lava",5)
403                          || !strncmp(tx->name,"*teleport",9)
404                          || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
405                                 tx->flags |= (SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA | SURF_CLIPSOLID);
406                 }
407                 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
408                         tx->flags |= (SURF_DRAWSKY | SURF_CLIPSOLID);
409                 else
410                 {
411                         tx->flags |= SURF_LIGHTMAP;
412                         if (!R_TextureHasAlpha(tx->texture))
413                                 tx->flags |= SURF_CLIPSOLID;
414                 }
415         }
416
417 //
418 // sequence the animations
419 //
420         for (i = 0;i < m->nummiptex;i++)
421         {
422                 tx = loadmodel->textures[i];
423                 if (!tx || tx->name[0] != '+')
424                         continue;
425                 if (tx->anim_total)
426                         continue;       // already sequenced
427
428                 // find the number of frames in the animation
429                 memset (anims, 0, sizeof(anims));
430                 memset (altanims, 0, sizeof(altanims));
431                 max = altmax = 0;
432
433                 for (j = i;j < m->nummiptex;j++)
434                 {
435                         tx2 = loadmodel->textures[j];
436                         if (!tx2 || tx2->name[0] != '+' || strcmp (tx2->name+2, tx->name+2))
437                                 continue;
438
439                         num = tx2->name[1];
440                         if (num >= '0' && num <= '9')
441                                 anims[num - '0'] = tx2;
442                         else if (num >= 'a' && num <= 'j')
443                                 altanims[num - 'a'] = tx2;
444                         else
445                                 Host_Error ("Bad animating texture %s", tx->name);
446                 }
447
448                 for (j = 0;j < 10;j++)
449                 {
450                         if (anims[j] != NULL)
451                                 max = j + 1;
452                         if (altanims[j] != NULL)
453                                 altmax = j + 1;
454                 }
455
456                 // link them all together
457                 for (j = 0;j < max;j++)
458                 {
459                         tx2 = anims[j];
460                         if (!tx2)
461                                 Host_Error ("Missing frame %i of %s", j, tx->name);
462                         tx2->anim_total = max;
463                         tx2->alternate_anims = altanims[0]; // NULL if there is no alternate
464                         for (k = 0;k < 10;k++)
465                                 tx2->anim_frames[k] = anims[k];
466                 }
467
468                 for (j = 0;j < altmax;j++)
469                 {
470                         tx2 = altanims[j];
471                         if (!tx2)
472                                 Host_Error ("Missing frame %i of %s", j, tx->name);
473                         tx2->anim_total = altmax;
474                         tx2->alternate_anims = anims[0]; // NULL if there is no alternate
475                         for (k = 0;k < 10;k++)
476                                 tx2->anim_frames[k] = altanims[k];
477                 }
478         }
479 }
480
481 /*
482 =================
483 Mod_LoadLighting
484 =================
485 */
486 static void Mod_LoadLighting (lump_t *l)
487 {
488         int i;
489         byte *in, *out, *data;
490         byte d;
491         char litfilename[1024];
492         loadmodel->lightdata = NULL;
493         if (loadmodel->ishlbsp) // LordHavoc: load the colored lighting data straight
494         {
495                 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
496                 memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
497         }
498         else // LordHavoc: bsp version 29 (normal white lighting)
499         {
500                 // LordHavoc: hope is not lost yet, check for a .lit file to load
501                 strcpy(litfilename, loadmodel->name);
502                 COM_StripExtension(litfilename, litfilename);
503                 strcat(litfilename, ".lit");
504                 data = (byte*) COM_LoadFile (litfilename, false);
505                 if (data)
506                 {
507                         if (loadsize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
508                         {
509                                 i = LittleLong(((int *)data)[1]);
510                                 if (i == 1)
511                                 {
512                                         Con_DPrintf("%s loaded", litfilename);
513                                         loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, loadsize - 8);
514                                         memcpy(loadmodel->lightdata, data + 8, loadsize - 8);
515                                         Mem_Free(data);
516                                         return;
517                                 }
518                                 else
519                                 {
520                                         Con_Printf("Unknown .lit file version (%d)\n", i);
521                                         Mem_Free(data);
522                                 }
523                         }
524                         else
525                         {
526                                 if (loadsize == 8)
527                                         Con_Printf("Empty .lit file, ignoring\n");
528                                 else
529                                         Con_Printf("Corrupt .lit file (old version?), ignoring\n");
530                                 Mem_Free(data);
531                         }
532                 }
533                 // LordHavoc: oh well, expand the white lighting data
534                 if (!l->filelen)
535                         return;
536                 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
537                 in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
538                 out = loadmodel->lightdata;
539                 memcpy (in, mod_base + l->fileofs, l->filelen);
540                 for (i = 0;i < l->filelen;i++)
541                 {
542                         d = *in++;
543                         *out++ = d;
544                         *out++ = d;
545                         *out++ = d;
546                 }
547         }
548 }
549
550
551 /*
552 =================
553 Mod_LoadVisibility
554 =================
555 */
556 static void Mod_LoadVisibility (lump_t *l)
557 {
558         if (!l->filelen)
559         {
560                 loadmodel->visdata = NULL;
561                 return;
562         }
563         loadmodel->visdata = Mem_Alloc(loadmodel->mempool, l->filelen);
564         memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
565 }
566
567 // used only for HalfLife maps
568 void Mod_ParseWadsFromEntityLump(char *data)
569 {
570         char key[128], value[4096];
571         char wadname[128];
572         int i, j, k;
573         if (!data)
574                 return;
575         data = COM_Parse(data);
576         if (!data)
577                 return; // error
578         if (com_token[0] != '{')
579                 return; // error
580         while (1)
581         {
582                 data = COM_Parse(data);
583                 if (!data)
584                         return; // error
585                 if (com_token[0] == '}')
586                         break; // end of worldspawn
587                 if (com_token[0] == '_')
588                         strcpy(key, com_token + 1);
589                 else
590                         strcpy(key, com_token);
591                 while (key[strlen(key)-1] == ' ') // remove trailing spaces
592                         key[strlen(key)-1] = 0;
593                 data = COM_Parse(data);
594                 if (!data)
595                         return; // error
596                 strcpy(value, com_token);
597                 if (!strcmp("wad", key)) // for HalfLife maps
598                 {
599                         if (loadmodel->ishlbsp)
600                         {
601                                 j = 0;
602                                 for (i = 0;i < 4096;i++)
603                                         if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
604                                                 break;
605                                 if (value[i])
606                                 {
607                                         for (;i < 4096;i++)
608                                         {
609                                                 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
610                                                 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
611                                                         j = i+1;
612                                                 else if (value[i] == ';' || value[i] == 0)
613                                                 {
614                                                         k = value[i];
615                                                         value[i] = 0;
616                                                         strcpy(wadname, "textures/");
617                                                         strcat(wadname, &value[j]);
618                                                         W_LoadTextureWadFile (wadname, false);
619                                                         j = i+1;
620                                                         if (!k)
621                                                                 break;
622                                                 }
623                                         }
624                                 }
625                         }
626                 }
627         }
628 }
629
630 /*
631 =================
632 Mod_LoadEntities
633 =================
634 */
635 static void Mod_LoadEntities (lump_t *l)
636 {
637         if (!l->filelen)
638         {
639                 loadmodel->entities = NULL;
640                 return;
641         }
642         loadmodel->entities = Mem_Alloc(loadmodel->mempool, l->filelen);
643         memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
644         if (loadmodel->ishlbsp)
645                 Mod_ParseWadsFromEntityLump(loadmodel->entities);
646 }
647
648
649 /*
650 =================
651 Mod_LoadVertexes
652 =================
653 */
654 static void Mod_LoadVertexes (lump_t *l)
655 {
656         dvertex_t       *in;
657         mvertex_t       *out;
658         int                     i, count;
659
660         in = (void *)(mod_base + l->fileofs);
661         if (l->filelen % sizeof(*in))
662                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
663         count = l->filelen / sizeof(*in);
664         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
665
666         loadmodel->vertexes = out;
667         loadmodel->numvertexes = count;
668
669         for ( i=0 ; i<count ; i++, in++, out++)
670         {
671                 out->position[0] = LittleFloat (in->point[0]);
672                 out->position[1] = LittleFloat (in->point[1]);
673                 out->position[2] = LittleFloat (in->point[2]);
674         }
675 }
676
677 /*
678 =================
679 Mod_LoadSubmodels
680 =================
681 */
682 static void Mod_LoadSubmodels (lump_t *l)
683 {
684         dmodel_t        *in;
685         dmodel_t        *out;
686         int                     i, j, count;
687
688         in = (void *)(mod_base + l->fileofs);
689         if (l->filelen % sizeof(*in))
690                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
691         count = l->filelen / sizeof(*in);
692         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
693
694         loadmodel->submodels = out;
695         loadmodel->numsubmodels = count;
696
697         for ( i=0 ; i<count ; i++, in++, out++)
698         {
699                 for (j=0 ; j<3 ; j++)
700                 {
701                         // spread the mins / maxs by a pixel
702                         out->mins[j] = LittleFloat (in->mins[j]) - 1;
703                         out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
704                         out->origin[j] = LittleFloat (in->origin[j]);
705                 }
706                 for (j=0 ; j<MAX_MAP_HULLS ; j++)
707                         out->headnode[j] = LittleLong (in->headnode[j]);
708                 out->visleafs = LittleLong (in->visleafs);
709                 out->firstface = LittleLong (in->firstface);
710                 out->numfaces = LittleLong (in->numfaces);
711         }
712 }
713
714 /*
715 =================
716 Mod_LoadEdges
717 =================
718 */
719 static void Mod_LoadEdges (lump_t *l)
720 {
721         dedge_t *in;
722         medge_t *out;
723         int     i, count;
724
725         in = (void *)(mod_base + l->fileofs);
726         if (l->filelen % sizeof(*in))
727                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
728         count = l->filelen / sizeof(*in);
729         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
730
731         loadmodel->edges = out;
732         loadmodel->numedges = count;
733
734         for ( i=0 ; i<count ; i++, in++, out++)
735         {
736                 out->v[0] = (unsigned short)LittleShort(in->v[0]);
737                 out->v[1] = (unsigned short)LittleShort(in->v[1]);
738         }
739 }
740
741 /*
742 =================
743 Mod_LoadTexinfo
744 =================
745 */
746 static void Mod_LoadTexinfo (lump_t *l)
747 {
748         texinfo_t *in;
749         mtexinfo_t *out;
750         int     i, j, k, count;
751         int             miptex;
752
753         in = (void *)(mod_base + l->fileofs);
754         if (l->filelen % sizeof(*in))
755                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
756         count = l->filelen / sizeof(*in);
757         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
758
759         loadmodel->texinfo = out;
760         loadmodel->numtexinfo = count;
761
762         for (i = 0;i < count;i++, in++, out++)
763         {
764                 for (k = 0;k < 2;k++)
765                         for (j = 0;j < 4;j++)
766                                 out->vecs[k][j] = LittleFloat (in->vecs[k][j]);
767
768                 miptex = LittleLong (in->miptex);
769                 out->flags = LittleLong (in->flags);
770
771                 if (!loadmodel->textures)
772                         out->texture = &loadmodel->notexture;
773                 else
774                 {
775                         if (miptex < 0)
776                                 Host_Error ("miptex < 0");
777                         if (miptex >= loadmodel->numtextures)
778                                 Host_Error ("miptex >= loadmodel->numtextures");
779                         out->texture = loadmodel->textures[miptex];
780                 }
781                 if (!out->texture)
782                         out->texture = &loadmodel->notexture;
783         }
784 }
785
786 /*
787 ================
788 CalcSurfaceExtents
789
790 Fills in s->texturemins[] and s->extents[]
791 ================
792 */
793 static void CalcSurfaceExtents (msurface_t *s)
794 {
795         float   mins[2], maxs[2], val;
796         int             i,j, e;
797         mvertex_t       *v;
798         mtexinfo_t      *tex;
799         int             bmins[2], bmaxs[2];
800
801         mins[0] = mins[1] = 999999999;
802         maxs[0] = maxs[1] = -999999999;
803
804         tex = s->texinfo;
805
806         for (i=0 ; i<s->numedges ; i++)
807         {
808                 e = loadmodel->surfedges[s->firstedge+i];
809                 if (e >= 0)
810                         v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
811                 else
812                         v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
813
814                 for (j=0 ; j<2 ; j++)
815                 {
816                         val = v->position[0] * tex->vecs[j][0] +
817                                 v->position[1] * tex->vecs[j][1] +
818                                 v->position[2] * tex->vecs[j][2] +
819                                 tex->vecs[j][3];
820                         if (val < mins[j])
821                                 mins[j] = val;
822                         if (val > maxs[j])
823                                 maxs[j] = val;
824                 }
825         }
826
827         for (i=0 ; i<2 ; i++)
828         {
829                 bmins[i] = floor(mins[i]/16);
830                 bmaxs[i] = ceil(maxs[i]/16);
831
832                 s->texturemins[i] = bmins[i] * 16;
833                 s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
834         }
835 }
836
837
838 void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs)
839 {
840         int             i, j;
841         float   *v;
842
843         mins[0] = mins[1] = mins[2] = 9999;
844         maxs[0] = maxs[1] = maxs[2] = -9999;
845         v = verts;
846         for (i = 0;i < numverts;i++)
847         {
848                 for (j = 0;j < 3;j++, v++)
849                 {
850                         if (*v < mins[j])
851                                 mins[j] = *v;
852                         if (*v > maxs[j])
853                                 maxs[j] = *v;
854                 }
855         }
856 }
857
858 #define MAX_SUBDIVPOLYTRIANGLES 4096
859 #define MAX_SUBDIVPOLYVERTS (MAX_SUBDIVPOLYTRIANGLES * 3)
860
861 static int subdivpolyverts, subdivpolytriangles;
862 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
863 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
864
865 static int subdivpolylookupvert(vec3_t v)
866 {
867         int i;
868         for (i = 0;i < subdivpolyverts;i++)
869                 if (subdivpolyvert[i][0] == v[0]
870                  && subdivpolyvert[i][1] == v[1]
871                  && subdivpolyvert[i][2] == v[2])
872                         return i;
873         if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
874                 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
875         VectorCopy(v, subdivpolyvert[subdivpolyverts]);
876         return subdivpolyverts++;
877 }
878
879 static void SubdividePolygon (int numverts, float *verts)
880 {
881         int             i, i1, i2, i3, f, b, c, p;
882         vec3_t  mins, maxs, front[256], back[256];
883         float   m, *pv, *cv, dist[256], frac;
884
885         if (numverts > 250)
886                 Host_Error ("SubdividePolygon: ran out of verts in buffer");
887
888         BoundPoly (numverts, verts, mins, maxs);
889
890         for (i = 0;i < 3;i++)
891         {
892                 m = (mins[i] + maxs[i]) * 0.5;
893                 m = r_subdivide_size.value * floor (m/r_subdivide_size.value + 0.5);
894                 if (maxs[i] - m < 8)
895                         continue;
896                 if (m - mins[i] < 8)
897                         continue;
898
899                 // cut it
900                 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
901                         dist[c] = cv[i] - m;
902
903                 f = b = 0;
904                 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
905                 {
906                         if (dist[p] >= 0)
907                         {
908                                 VectorCopy (pv, front[f]);
909                                 f++;
910                         }
911                         if (dist[p] <= 0)
912                         {
913                                 VectorCopy (pv, back[b]);
914                                 b++;
915                         }
916                         if (dist[p] == 0 || dist[c] == 0)
917                                 continue;
918                         if ( (dist[p] > 0) != (dist[c] > 0) )
919                         {
920                                 // clip point
921                                 frac = dist[p] / (dist[p] - dist[c]);
922                                 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
923                                 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
924                                 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
925                                 f++;
926                                 b++;
927                         }
928                 }
929
930                 SubdividePolygon (f, front[0]);
931                 SubdividePolygon (b, back[0]);
932                 return;
933         }
934
935         i1 = subdivpolylookupvert(verts);
936         i2 = subdivpolylookupvert(verts + 3);
937         for (i = 2;i < numverts;i++)
938         {
939                 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
940                 {
941                         Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
942                         return;
943                 }
944
945                 i3 = subdivpolylookupvert(verts + i * 3);
946                 subdivpolyindex[subdivpolytriangles][0] = i1;
947                 subdivpolyindex[subdivpolytriangles][1] = i2;
948                 subdivpolyindex[subdivpolytriangles][2] = i3;
949                 i2 = i3;
950                 subdivpolytriangles++;
951         }
952 }
953
954 /*
955 ================
956 Mod_GenerateWarpMesh
957
958 Breaks a polygon up along axial 64 unit
959 boundaries so that turbulent and sky warps
960 can be done reasonably.
961 ================
962 */
963 void Mod_GenerateWarpMesh (msurface_t *surf)
964 {
965         int                             i, j;
966         surfvertex_t    *v;
967         surfmesh_t              *mesh;
968
969         subdivpolytriangles = 0;
970         subdivpolyverts = 0;
971         SubdividePolygon (surf->poly_numverts, surf->poly_verts);
972
973         mesh = &surf->mesh;
974         mesh->numverts = subdivpolyverts;
975         mesh->numtriangles = subdivpolytriangles;
976         if (mesh->numtriangles < 1)
977                 Host_Error("Mod_GenerateWarpMesh: no triangles?\n");
978         mesh->index = Mem_Alloc(loadmodel->mempool, mesh->numtriangles * sizeof(int[3]) + mesh->numverts * sizeof(surfvertex_t));
979         mesh->vertex = (surfvertex_t *)((long) mesh->index + mesh->numtriangles * sizeof(int[3]));
980         memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
981
982         for (i = 0;i < mesh->numtriangles;i++)
983         {
984                 for (j = 0;j < 3;j++)
985                 {
986                         mesh->index[i*3+j] = subdivpolyindex[i][j];
987                         //if (mesh->index[i] < 0 || mesh->index[i] >= mesh->numverts)
988                         //      Host_Error("Mod_GenerateWarpMesh: invalid index generated\n");
989                 }
990         }
991
992         for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
993         {
994                 VectorCopy(subdivpolyvert[i], v->v);
995                 v->st[0] = DotProduct (v->v, surf->texinfo->vecs[0]);
996                 v->st[1] = DotProduct (v->v, surf->texinfo->vecs[1]);
997         }
998 }
999
1000 void Mod_GenerateVertexLitMesh (msurface_t *surf)
1001 {
1002         int                             i, is, it, *index, smax, tmax;
1003         float                   *in, s, t;
1004         surfvertex_t    *out;
1005         surfmesh_t              *mesh;
1006
1007         //surf->flags |= SURF_LIGHTMAP;
1008         smax = surf->extents[0] >> 4;
1009         tmax = surf->extents[1] >> 4;
1010         surf->lightmaptexturestride = 0;
1011         surf->lightmaptexture = NULL;
1012
1013         mesh = &surf->mesh;
1014         mesh->numverts = surf->poly_numverts;
1015         mesh->numtriangles = surf->poly_numverts - 2;
1016         mesh->index = Mem_Alloc(loadmodel->mempool, mesh->numtriangles * sizeof(int[3]) + mesh->numverts * sizeof(surfvertex_t));
1017         mesh->vertex = (surfvertex_t *)((long) mesh->index + mesh->numtriangles * sizeof(int[3]));
1018         memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1019
1020         index = mesh->index;
1021         for (i = 0;i < mesh->numtriangles;i++)
1022         {
1023                 *index++ = 0;
1024                 *index++ = i + 1;
1025                 *index++ = i + 2;
1026         }
1027
1028         for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1029         {
1030                 VectorCopy (in, out->v);
1031
1032                 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1033                 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1034
1035                 out->st[0] = s / surf->texinfo->texture->width;
1036                 out->st[1] = t / surf->texinfo->texture->height;
1037
1038                 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1039                 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1040
1041                 // lightmap coordinates
1042                 out->uv[0] = 0;
1043                 out->uv[1] = 0;
1044
1045                 // LordHavoc: calc lightmap data offset for vertex lighting to use
1046                 is = (int) s;
1047                 it = (int) t;
1048                 is = bound(0, is, smax);
1049                 it = bound(0, it, tmax);
1050                 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1051         }
1052 }
1053
1054 void Mod_GenerateLightmappedMesh (msurface_t *surf)
1055 {
1056         int                             i, is, it, *index, smax, tmax;
1057         float                   *in, s, t, xbase, ybase, xscale, yscale;
1058         surfvertex_t    *out;
1059         surfmesh_t              *mesh;
1060
1061         surf->flags |= SURF_LIGHTMAP;
1062         smax = surf->extents[0] >> 4;
1063         tmax = surf->extents[1] >> 4;
1064         if (r_miplightmaps.integer)
1065         {
1066                 surf->lightmaptexturestride = (surf->extents[0]>>4)+1;
1067                 surf->lightmaptexture = R_ProceduralTexture(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_MIPMAP | TEXF_PRECACHE, NULL, NULL, 0);
1068         }
1069         else
1070         {
1071                 surf->lightmaptexturestride = R_CompatibleFragmentWidth((surf->extents[0]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1072                 surf->lightmaptexture = R_ProceduralTexture(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FRAGMENT | TEXF_PRECACHE, NULL, NULL, 0);
1073         }
1074 //      surf->lightmaptexture = R_LoadTexture(loadmodel->texturepool, va("lightmap%08x", lightmapnum), surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FRAGMENT | TEXF_PRECACHE);
1075 //      surf->lightmaptexture = R_LoadTexture(loadmodel->texturepool, va("lightmap%08x", lightmapnum), surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_PRECACHE);
1076         R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &xbase, &ybase, &xscale, &yscale);
1077         xscale = (xscale - xbase) * 16.0 / ((surf->extents[0] & ~15) + 16);
1078         yscale = (yscale - ybase) * 16.0 / ((surf->extents[1] & ~15) + 16);
1079
1080         mesh = &surf->mesh;
1081         mesh->numverts = surf->poly_numverts;
1082         mesh->numtriangles = surf->poly_numverts - 2;
1083         mesh->index = Mem_Alloc(loadmodel->mempool, mesh->numtriangles * sizeof(int[3]) + mesh->numverts * sizeof(surfvertex_t));
1084         mesh->vertex = (surfvertex_t *)((long) mesh->index + mesh->numtriangles * sizeof(int[3]));
1085         memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1086
1087         index = mesh->index;
1088         for (i = 0;i < mesh->numtriangles;i++)
1089         {
1090                 *index++ = 0;
1091                 *index++ = i + 1;
1092                 *index++ = i + 2;
1093         }
1094
1095         for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1096         {
1097                 VectorCopy (in, out->v);
1098
1099                 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1100                 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1101
1102                 out->st[0] = s / surf->texinfo->texture->width;
1103                 out->st[1] = t / surf->texinfo->texture->height;
1104
1105                 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1106                 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1107
1108                 // lightmap coordinates
1109                 out->uv[0] = s * xscale + xbase;
1110                 out->uv[1] = t * yscale + ybase;
1111
1112                 // LordHavoc: calc lightmap data offset for vertex lighting to use
1113                 is = (int) s;
1114                 it = (int) t;
1115                 is = bound(0, is, smax);
1116                 it = bound(0, it, tmax);
1117                 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1118         }
1119 }
1120
1121 void Mod_GenerateVertexMesh (msurface_t *surf)
1122 {
1123         int                             i, *index;
1124         float                   *in;
1125         surfvertex_t    *out;
1126         surfmesh_t              *mesh;
1127
1128         surf->lightmaptexturestride = 0;
1129         surf->lightmaptexture = NULL;
1130
1131         mesh = &surf->mesh;
1132         mesh->numverts = surf->poly_numverts;
1133         mesh->numtriangles = surf->poly_numverts - 2;
1134         mesh->index = Mem_Alloc(loadmodel->mempool, mesh->numtriangles * sizeof(int[3]) + mesh->numverts * sizeof(surfvertex_t));
1135         mesh->vertex = (surfvertex_t *)((long) mesh->index + mesh->numtriangles * sizeof(int[3]));
1136         memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1137
1138         index = mesh->index;
1139         for (i = 0;i < mesh->numtriangles;i++)
1140         {
1141                 *index++ = 0;
1142                 *index++ = i + 1;
1143                 *index++ = i + 2;
1144         }
1145
1146         for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1147         {
1148                 VectorCopy (in, out->v);
1149                 out->st[0] = (DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) / surf->texinfo->texture->width;
1150                 out->st[1] = (DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) / surf->texinfo->texture->height;
1151         }
1152 }
1153
1154 void Mod_GenerateSurfacePolygon (msurface_t *surf)
1155 {
1156         float           *vert;
1157         int                     i;
1158         int                     lindex;
1159         float           *vec;
1160
1161         // convert edges back to a normal polygon
1162         surf->poly_numverts = surf->numedges;
1163         vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges);
1164         for (i = 0;i < surf->numedges;i++)
1165         {
1166                 lindex = loadmodel->surfedges[surf->firstedge + i];
1167                 if (lindex > 0)
1168                         vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
1169                 else
1170                         vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
1171                 VectorCopy (vec, vert);
1172                 vert += 3;
1173         }
1174 }
1175
1176 /*
1177 =================
1178 Mod_LoadFaces
1179 =================
1180 */
1181 static void Mod_LoadFaces (lump_t *l)
1182 {
1183         dface_t         *in;
1184         msurface_t      *out;
1185         int                     i, count, surfnum, planenum, side, ssize, tsize;
1186
1187         in = (void *)(mod_base + l->fileofs);
1188         if (l->filelen % sizeof(*in))
1189                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1190         count = l->filelen / sizeof(*in);
1191         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1192
1193         loadmodel->surfaces = out;
1194         loadmodel->numsurfaces = count;
1195
1196         for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
1197         {
1198                 // FIXME: validate edges, texinfo, etc?
1199                 out->firstedge = LittleLong(in->firstedge);
1200                 out->numedges = LittleShort(in->numedges);
1201
1202                 out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo);
1203                 out->flags = out->texinfo->texture->flags;
1204
1205                 planenum = LittleShort(in->planenum);
1206                 side = LittleShort(in->side);
1207                 if (side)
1208                         out->flags |= SURF_PLANEBACK;
1209
1210                 out->plane = loadmodel->planes + planenum;
1211
1212                 // clear lightmap (filled in later)
1213                 out->lightmaptexture = NULL;
1214
1215                 // force lightmap upload on first time seeing the surface
1216                 out->cached_dlight = true;
1217                 out->cached_ambient = -1000;
1218                 out->cached_lightscalebit = -1000;
1219
1220                 CalcSurfaceExtents (out);
1221
1222                 ssize = (out->extents[0] >> 4) + 1;
1223                 tsize = (out->extents[1] >> 4) + 1;
1224
1225                 // lighting info
1226                 for (i = 0;i < MAXLIGHTMAPS;i++)
1227                         out->styles[i] = in->styles[i];
1228                 i = LittleLong(in->lightofs);
1229                 if (i == -1)
1230                         out->samples = NULL;
1231                 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1232                         out->samples = loadmodel->lightdata + i;
1233                 else // LordHavoc: white lighting (bsp version 29)
1234                         out->samples = loadmodel->lightdata + (i * 3);
1235
1236                 Mod_GenerateSurfacePolygon(out);
1237
1238                 if (out->texinfo->texture->flags & SURF_DRAWSKY)
1239                 {
1240                         out->shader = &Cshader_sky;
1241                         out->samples = NULL;
1242                         Mod_GenerateWarpMesh (out);
1243                         continue;
1244                 }
1245
1246                 if (out->texinfo->texture->flags & SURF_DRAWTURB)
1247                 {
1248                         out->shader = &Cshader_water;
1249                         /*
1250                         for (i=0 ; i<2 ; i++)
1251                         {
1252                                 out->extents[i] = 16384*1024;
1253                                 out->texturemins[i] = -8192*1024;
1254                         }
1255                         */
1256                         out->samples = NULL;
1257                         Mod_GenerateWarpMesh (out);
1258                         continue;
1259                 }
1260
1261                 if (!R_TextureHasAlpha(out->texinfo->texture->texture))
1262                         out->flags |= SURF_CLIPSOLID;
1263                 if (out->texinfo->flags & TEX_SPECIAL)
1264                 {
1265                         // qbsp couldn't find the texture for this surface, but it was either turb or sky...  assume turb
1266                         out->shader = &Cshader_water;
1267                         out->samples = NULL;
1268                         Mod_GenerateWarpMesh (out);
1269                 }
1270                 else if ((out->extents[0]+1) > (256*16) || (out->extents[1]+1) > (256*16))
1271                 {
1272                         Con_Printf ("Bad surface extents, converting to fullbright polygon");
1273                         out->shader = &Cshader_wall_fullbright;
1274                         out->samples = NULL;
1275                         Mod_GenerateVertexMesh(out);
1276                 }
1277                 else
1278                 {
1279                         // stainmap for permanent marks on walls
1280                         out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1281                         // clear to white
1282                         memset(out->stainsamples, 255, ssize * tsize * 3);
1283                         if (out->extents[0] < r_vertexsurfacesthreshold.integer && out->extents[1] < r_vertexsurfacesthreshold.integer)
1284                         {
1285                                 out->shader = &Cshader_wall_vertex;
1286                                 Mod_GenerateVertexLitMesh(out);
1287                         }
1288                         else
1289                         {
1290                                 out->shader = &Cshader_wall_lightmap;
1291                                 Mod_GenerateLightmappedMesh(out);
1292                         }
1293                 }
1294         }
1295 }
1296
1297 static model_t *sortmodel;
1298
1299 static int Mod_SurfaceQSortCompare(const void *voida, const void *voidb)
1300 {
1301         const msurface_t *a, *b;
1302         a = *((const msurface_t **)voida);
1303         b = *((const msurface_t **)voidb);
1304         if (a->shader != b->shader)
1305                 return (long) a->shader - (long) b->shader;
1306         if (a->texinfo->texture != b->texinfo->texture);
1307                 return a->texinfo->texture - b->texinfo->texture;
1308         return 0;
1309 }
1310
1311 static void Mod_BrushSortedSurfaces(model_t *model, mempool_t *pool)
1312 {
1313         int surfnum;
1314         sortmodel = model;
1315         sortmodel->modelsortedsurfaces = Mem_Alloc(pool, sortmodel->nummodelsurfaces * sizeof(msurface_t *));
1316         for (surfnum = 0;surfnum < sortmodel->nummodelsurfaces;surfnum++)
1317                 sortmodel->modelsortedsurfaces[surfnum] = &sortmodel->surfaces[surfnum + sortmodel->firstmodelsurface];
1318
1319         qsort(sortmodel->modelsortedsurfaces, sortmodel->nummodelsurfaces, sizeof(msurface_t *), Mod_SurfaceQSortCompare);
1320 }
1321
1322
1323 /*
1324 =================
1325 Mod_SetParent
1326 =================
1327 */
1328 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1329 {
1330         node->parent = parent;
1331         if (node->contents < 0)
1332                 return;
1333         Mod_SetParent (node->children[0], node);
1334         Mod_SetParent (node->children[1], node);
1335 }
1336
1337 /*
1338 =================
1339 Mod_LoadNodes
1340 =================
1341 */
1342 static void Mod_LoadNodes (lump_t *l)
1343 {
1344         int                     i, j, count, p;
1345         dnode_t         *in;
1346         mnode_t         *out;
1347
1348         in = (void *)(mod_base + l->fileofs);
1349         if (l->filelen % sizeof(*in))
1350                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1351         count = l->filelen / sizeof(*in);
1352         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1353
1354         loadmodel->nodes = out;
1355         loadmodel->numnodes = count;
1356
1357         for ( i=0 ; i<count ; i++, in++, out++)
1358         {
1359                 for (j=0 ; j<3 ; j++)
1360                 {
1361                         out->mins[j] = LittleShort (in->mins[j]);
1362                         out->maxs[j] = LittleShort (in->maxs[j]);
1363                 }
1364
1365                 p = LittleLong(in->planenum);
1366                 out->plane = loadmodel->planes + p;
1367
1368                 out->firstsurface = LittleShort (in->firstface);
1369                 out->numsurfaces = LittleShort (in->numfaces);
1370
1371                 for (j=0 ; j<2 ; j++)
1372                 {
1373                         p = LittleShort (in->children[j]);
1374                         if (p >= 0)
1375                                 out->children[j] = loadmodel->nodes + p;
1376                         else
1377                                 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1378                 }
1379         }
1380
1381         Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1382 }
1383
1384 /*
1385 =================
1386 Mod_LoadLeafs
1387 =================
1388 */
1389 static void Mod_LoadLeafs (lump_t *l)
1390 {
1391         dleaf_t         *in;
1392         mleaf_t         *out;
1393         int                     i, j, count, p;
1394
1395         in = (void *)(mod_base + l->fileofs);
1396         if (l->filelen % sizeof(*in))
1397                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1398         count = l->filelen / sizeof(*in);
1399         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1400
1401         loadmodel->leafs = out;
1402         loadmodel->numleafs = count;
1403
1404         for ( i=0 ; i<count ; i++, in++, out++)
1405         {
1406                 for (j=0 ; j<3 ; j++)
1407                 {
1408                         out->mins[j] = LittleShort (in->mins[j]);
1409                         out->maxs[j] = LittleShort (in->maxs[j]);
1410                 }
1411
1412                 p = LittleLong(in->contents);
1413                 out->contents = p;
1414
1415                 out->firstmarksurface = loadmodel->marksurfaces +
1416                         LittleShort(in->firstmarksurface);
1417                 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1418
1419                 p = LittleLong(in->visofs);
1420                 if (p == -1)
1421                         out->compressed_vis = NULL;
1422                 else
1423                         out->compressed_vis = loadmodel->visdata + p;
1424
1425                 for (j=0 ; j<4 ; j++)
1426                         out->ambient_sound_level[j] = in->ambient_level[j];
1427
1428                 // gl underwater warp
1429                 // LordHavoc: disabled underwater warping
1430                 /*
1431                 if (out->contents != CONTENTS_EMPTY)
1432                 {
1433                         for (j=0 ; j<out->nummarksurfaces ; j++)
1434                                 out->firstmarksurface[j]->flags |= SURF_UNDERWATER;
1435                 }
1436                 */
1437         }
1438 }
1439
1440 /*
1441 =================
1442 Mod_LoadClipnodes
1443 =================
1444 */
1445 static void Mod_LoadClipnodes (lump_t *l)
1446 {
1447         dclipnode_t *in, *out;
1448         int                     i, count;
1449         hull_t          *hull;
1450
1451         in = (void *)(mod_base + l->fileofs);
1452         if (l->filelen % sizeof(*in))
1453                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1454         count = l->filelen / sizeof(*in);
1455         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1456
1457         loadmodel->clipnodes = out;
1458         loadmodel->numclipnodes = count;
1459
1460         if (loadmodel->ishlbsp)
1461         {
1462                 hull = &loadmodel->hulls[1];
1463                 hull->clipnodes = out;
1464                 hull->firstclipnode = 0;
1465                 hull->lastclipnode = count-1;
1466                 hull->planes = loadmodel->planes;
1467                 hull->clip_mins[0] = -16;
1468                 hull->clip_mins[1] = -16;
1469                 hull->clip_mins[2] = -36;
1470                 hull->clip_maxs[0] = 16;
1471                 hull->clip_maxs[1] = 16;
1472                 hull->clip_maxs[2] = 36;
1473
1474                 hull = &loadmodel->hulls[2];
1475                 hull->clipnodes = out;
1476                 hull->firstclipnode = 0;
1477                 hull->lastclipnode = count-1;
1478                 hull->planes = loadmodel->planes;
1479                 hull->clip_mins[0] = -32;
1480                 hull->clip_mins[1] = -32;
1481                 hull->clip_mins[2] = -32;
1482                 hull->clip_maxs[0] = 32;
1483                 hull->clip_maxs[1] = 32;
1484                 hull->clip_maxs[2] = 32;
1485
1486                 hull = &loadmodel->hulls[3];
1487                 hull->clipnodes = out;
1488                 hull->firstclipnode = 0;
1489                 hull->lastclipnode = count-1;
1490                 hull->planes = loadmodel->planes;
1491                 hull->clip_mins[0] = -16;
1492                 hull->clip_mins[1] = -16;
1493                 hull->clip_mins[2] = -18;
1494                 hull->clip_maxs[0] = 16;
1495                 hull->clip_maxs[1] = 16;
1496                 hull->clip_maxs[2] = 18;
1497         }
1498         else
1499         {
1500                 hull = &loadmodel->hulls[1];
1501                 hull->clipnodes = out;
1502                 hull->firstclipnode = 0;
1503                 hull->lastclipnode = count-1;
1504                 hull->planes = loadmodel->planes;
1505                 hull->clip_mins[0] = -16;
1506                 hull->clip_mins[1] = -16;
1507                 hull->clip_mins[2] = -24;
1508                 hull->clip_maxs[0] = 16;
1509                 hull->clip_maxs[1] = 16;
1510                 hull->clip_maxs[2] = 32;
1511
1512                 hull = &loadmodel->hulls[2];
1513                 hull->clipnodes = out;
1514                 hull->firstclipnode = 0;
1515                 hull->lastclipnode = count-1;
1516                 hull->planes = loadmodel->planes;
1517                 hull->clip_mins[0] = -32;
1518                 hull->clip_mins[1] = -32;
1519                 hull->clip_mins[2] = -24;
1520                 hull->clip_maxs[0] = 32;
1521                 hull->clip_maxs[1] = 32;
1522                 hull->clip_maxs[2] = 64;
1523         }
1524
1525         for (i=0 ; i<count ; i++, out++, in++)
1526         {
1527                 out->planenum = LittleLong(in->planenum);
1528                 out->children[0] = LittleShort(in->children[0]);
1529                 out->children[1] = LittleShort(in->children[1]);
1530                 if (out->children[0] >= count || out->children[1] >= count)
1531                         Host_Error("Corrupt clipping hull (out of range child)\n");
1532         }
1533 }
1534
1535 /*
1536 =================
1537 Mod_MakeHull0
1538
1539 Duplicate the drawing hull structure as a clipping hull
1540 =================
1541 */
1542 static void Mod_MakeHull0 (void)
1543 {
1544         mnode_t         *in;
1545         dclipnode_t *out;
1546         int                     i;
1547         hull_t          *hull;
1548
1549         hull = &loadmodel->hulls[0];
1550
1551         in = loadmodel->nodes;
1552         out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1553
1554         hull->clipnodes = out;
1555         hull->firstclipnode = 0;
1556         hull->lastclipnode = loadmodel->numnodes - 1;
1557         hull->planes = loadmodel->planes;
1558
1559         for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1560         {
1561                 out->planenum = in->plane - loadmodel->planes;
1562                 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1563                 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1564         }
1565 }
1566
1567 /*
1568 =================
1569 Mod_LoadMarksurfaces
1570 =================
1571 */
1572 static void Mod_LoadMarksurfaces (lump_t *l)
1573 {
1574         int             i, j;
1575         short   *in;
1576
1577         in = (void *)(mod_base + l->fileofs);
1578         if (l->filelen % sizeof(*in))
1579                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1580         loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1581         loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(msurface_t *));
1582
1583         for (i = 0;i < loadmodel->nummarksurfaces;i++)
1584         {
1585                 j = (unsigned) LittleShort(in[i]);
1586                 if (j >= loadmodel->numsurfaces)
1587                         Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1588                 loadmodel->marksurfaces[i] = loadmodel->surfaces + j;
1589         }
1590 }
1591
1592 /*
1593 =================
1594 Mod_LoadSurfedges
1595 =================
1596 */
1597 static void Mod_LoadSurfedges (lump_t *l)
1598 {
1599         int             i;
1600         int             *in;
1601
1602         in = (void *)(mod_base + l->fileofs);
1603         if (l->filelen % sizeof(*in))
1604                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1605         loadmodel->numsurfedges = l->filelen / sizeof(*in);
1606         loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1607
1608         for (i = 0;i < loadmodel->numsurfedges;i++)
1609                 loadmodel->surfedges[i] = LittleLong (in[i]);
1610 }
1611
1612
1613 /*
1614 =================
1615 Mod_LoadPlanes
1616 =================
1617 */
1618 static void Mod_LoadPlanes (lump_t *l)
1619 {
1620         int                     i;
1621         mplane_t        *out;
1622         dplane_t        *in;
1623
1624         in = (void *)(mod_base + l->fileofs);
1625         if (l->filelen % sizeof(*in))
1626                 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
1627
1628         loadmodel->numplanes = l->filelen / sizeof(*in);
1629         loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
1630
1631         for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
1632         {
1633                 out->normal[0] = LittleFloat (in->normal[0]);
1634                 out->normal[1] = LittleFloat (in->normal[1]);
1635                 out->normal[2] = LittleFloat (in->normal[2]);
1636                 out->dist = LittleFloat (in->dist);
1637
1638                 // LordHavoc: recalculated by PlaneClassify, FIXME: validate type and report error if type does not match normal?
1639 //              out->type = LittleLong (in->type);
1640                 PlaneClassify(out);
1641         }
1642 }
1643
1644 #define MAX_POINTS_ON_WINDING 64
1645
1646 typedef struct
1647 {
1648         int numpoints;
1649         int padding;
1650         double points[8][3]; // variable sized
1651 }
1652 winding_t;
1653
1654 /*
1655 ==================
1656 NewWinding
1657 ==================
1658 */
1659 static winding_t *NewWinding (int points)
1660 {
1661         winding_t *w;
1662         int size;
1663
1664         if (points > MAX_POINTS_ON_WINDING)
1665                 Sys_Error("NewWinding: too many points\n");
1666
1667         size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1668         w = Mem_Alloc(loadmodel->mempool, size);
1669         memset (w, 0, size);
1670
1671         return w;
1672 }
1673
1674 static void FreeWinding (winding_t *w)
1675 {
1676         Mem_Free(w);
1677 }
1678
1679 /*
1680 =================
1681 BaseWindingForPlane
1682 =================
1683 */
1684 static winding_t *BaseWindingForPlane (mplane_t *p)
1685 {
1686         double org[3], vright[3], vup[3], normal[3];
1687         winding_t *w;
1688
1689         VectorCopy(p->normal, normal);
1690         VectorVectorsDouble(normal, vright, vup);
1691
1692         VectorScale (vup, 1024.0*1024.0*1024.0, vup);
1693         VectorScale (vright, 1024.0*1024.0*1024.0, vright);
1694
1695         // project a really big axis aligned box onto the plane
1696         w = NewWinding (4);
1697
1698         VectorScale (p->normal, p->dist, org);
1699
1700         VectorSubtract (org, vright, w->points[0]);
1701         VectorAdd (w->points[0], vup, w->points[0]);
1702
1703         VectorAdd (org, vright, w->points[1]);
1704         VectorAdd (w->points[1], vup, w->points[1]);
1705
1706         VectorAdd (org, vright, w->points[2]);
1707         VectorSubtract (w->points[2], vup, w->points[2]);
1708
1709         VectorSubtract (org, vright, w->points[3]);
1710         VectorSubtract (w->points[3], vup, w->points[3]);
1711
1712         w->numpoints = 4;
1713
1714         return w;
1715 }
1716
1717 /*
1718 ==================
1719 ClipWinding
1720
1721 Clips the winding to the plane, returning the new winding on the positive side
1722 Frees the input winding.
1723 If keepon is true, an exactly on-plane winding will be saved, otherwise
1724 it will be clipped away.
1725 ==================
1726 */
1727 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1728 {
1729         double  dists[MAX_POINTS_ON_WINDING + 1];
1730         int             sides[MAX_POINTS_ON_WINDING + 1];
1731         int             counts[3];
1732         double  dot;
1733         int             i, j;
1734         double  *p1, *p2;
1735         double  mid[3];
1736         winding_t       *neww;
1737         int             maxpts;
1738
1739         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1740
1741         // determine sides for each point
1742         for (i = 0;i < in->numpoints;i++)
1743         {
1744                 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1745                 if (dot > ON_EPSILON)
1746                         sides[i] = SIDE_FRONT;
1747                 else if (dot < -ON_EPSILON)
1748                         sides[i] = SIDE_BACK;
1749                 else
1750                         sides[i] = SIDE_ON;
1751                 counts[sides[i]]++;
1752         }
1753         sides[i] = sides[0];
1754         dists[i] = dists[0];
1755
1756         if (keepon && !counts[0] && !counts[1])
1757                 return in;
1758
1759         if (!counts[0])
1760         {
1761                 FreeWinding (in);
1762                 return NULL;
1763         }
1764         if (!counts[1])
1765                 return in;
1766
1767         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
1768         if (maxpts > MAX_POINTS_ON_WINDING)
1769                 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1770
1771         neww = NewWinding (maxpts);
1772
1773         for (i = 0;i < in->numpoints;i++)
1774         {
1775                 if (neww->numpoints >= maxpts)
1776                         Sys_Error ("ClipWinding: points exceeded estimate");
1777
1778                 p1 = in->points[i];
1779
1780                 if (sides[i] == SIDE_ON)
1781                 {
1782                         VectorCopy (p1, neww->points[neww->numpoints]);
1783                         neww->numpoints++;
1784                         continue;
1785                 }
1786
1787                 if (sides[i] == SIDE_FRONT)
1788                 {
1789                         VectorCopy (p1, neww->points[neww->numpoints]);
1790                         neww->numpoints++;
1791                 }
1792
1793                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1794                         continue;
1795
1796                 // generate a split point
1797                 p2 = in->points[(i+1)%in->numpoints];
1798
1799                 dot = dists[i] / (dists[i]-dists[i+1]);
1800                 for (j = 0;j < 3;j++)
1801                 {       // avoid round off error when possible
1802                         if (split->normal[j] == 1)
1803                                 mid[j] = split->dist;
1804                         else if (split->normal[j] == -1)
1805                                 mid[j] = -split->dist;
1806                         else
1807                                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1808                 }
1809
1810                 VectorCopy (mid, neww->points[neww->numpoints]);
1811                 neww->numpoints++;
1812         }
1813
1814         // free the original winding
1815         FreeWinding (in);
1816
1817         // debugging
1818         //Mem_CheckSentinels(neww);
1819
1820         return neww;
1821 }
1822
1823
1824 /*
1825 ==================
1826 DivideWinding
1827
1828 Divides a winding by a plane, producing one or two windings.  The
1829 original winding is not damaged or freed.  If only on one side, the
1830 returned winding will be the input winding.  If on both sides, two
1831 new windings will be created.
1832 ==================
1833 */
1834 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1835 {
1836         double  dists[MAX_POINTS_ON_WINDING + 1];
1837         int             sides[MAX_POINTS_ON_WINDING + 1];
1838         int             counts[3];
1839         double  dot;
1840         int             i, j;
1841         double  *p1, *p2;
1842         double  mid[3];
1843         winding_t       *f, *b;
1844         int             maxpts;
1845
1846         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1847
1848         // determine sides for each point
1849         for (i = 0;i < in->numpoints;i++)
1850         {
1851                 dot = DotProduct (in->points[i], split->normal);
1852                 dot -= split->dist;
1853                 dists[i] = dot;
1854                 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1855                 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1856                 else sides[i] = SIDE_ON;
1857                 counts[sides[i]]++;
1858         }
1859         sides[i] = sides[0];
1860         dists[i] = dists[0];
1861
1862         *front = *back = NULL;
1863
1864         if (!counts[0])
1865         {
1866                 *back = in;
1867                 return;
1868         }
1869         if (!counts[1])
1870         {
1871                 *front = in;
1872                 return;
1873         }
1874
1875         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
1876
1877         if (maxpts > MAX_POINTS_ON_WINDING)
1878                 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1879
1880         *front = f = NewWinding (maxpts);
1881         *back = b = NewWinding (maxpts);
1882
1883         for (i = 0;i < in->numpoints;i++)
1884         {
1885                 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
1886                         Sys_Error ("DivideWinding: points exceeded estimate");
1887
1888                 p1 = in->points[i];
1889
1890                 if (sides[i] == SIDE_ON)
1891                 {
1892                         VectorCopy (p1, f->points[f->numpoints]);
1893                         f->numpoints++;
1894                         VectorCopy (p1, b->points[b->numpoints]);
1895                         b->numpoints++;
1896                         continue;
1897                 }
1898
1899                 if (sides[i] == SIDE_FRONT)
1900                 {
1901                         VectorCopy (p1, f->points[f->numpoints]);
1902                         f->numpoints++;
1903                 }
1904                 else if (sides[i] == SIDE_BACK)
1905                 {
1906                         VectorCopy (p1, b->points[b->numpoints]);
1907                         b->numpoints++;
1908                 }
1909
1910                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1911                         continue;
1912
1913                 // generate a split point
1914                 p2 = in->points[(i+1)%in->numpoints];
1915
1916                 dot = dists[i] / (dists[i]-dists[i+1]);
1917                 for (j = 0;j < 3;j++)
1918                 {       // avoid round off error when possible
1919                         if (split->normal[j] == 1)
1920                                 mid[j] = split->dist;
1921                         else if (split->normal[j] == -1)
1922                                 mid[j] = -split->dist;
1923                         else
1924                                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1925                 }
1926
1927                 VectorCopy (mid, f->points[f->numpoints]);
1928                 f->numpoints++;
1929                 VectorCopy (mid, b->points[b->numpoints]);
1930                 b->numpoints++;
1931         }
1932
1933         // debugging
1934         //Mem_CheckSentinels(f);
1935         //Mem_CheckSentinels(b);
1936 }
1937
1938 typedef struct portal_s
1939 {
1940         mplane_t plane;
1941         mnode_t *nodes[2];              // [0] = front side of plane
1942         struct portal_s *next[2];
1943         winding_t *winding;
1944         struct portal_s *chain; // all portals are linked into a list
1945 }
1946 portal_t;
1947
1948 static portal_t *portalchain;
1949
1950 /*
1951 ===========
1952 AllocPortal
1953 ===========
1954 */
1955 static portal_t *AllocPortal (void)
1956 {
1957         portal_t *p;
1958         p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
1959         //memset(p, 0, sizeof(portal_t));
1960         p->chain = portalchain;
1961         portalchain = p;
1962         return p;
1963 }
1964
1965 static void FreePortal(portal_t *p)
1966 {
1967         Mem_Free(p);
1968 }
1969
1970 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
1971 {
1972         // calculate children first
1973         if (node->children[0]->contents >= 0)
1974                 Mod_RecursiveRecalcNodeBBox(node->children[0]);
1975         if (node->children[1]->contents >= 0)
1976                 Mod_RecursiveRecalcNodeBBox(node->children[1]);
1977
1978         // make combined bounding box from children
1979         node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
1980         node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
1981         node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
1982         node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
1983         node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
1984         node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
1985 }
1986
1987 static void Mod_FinalizePortals(void)
1988 {
1989         int i, j, numportals, numpoints;
1990         portal_t *p, *pnext;
1991         mportal_t *portal;
1992         mvertex_t *point;
1993         mleaf_t *leaf, *endleaf;
1994         winding_t *w;
1995
1996         //Mem_CheckSentinelsGlobal();
1997
1998         // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
1999         leaf = loadmodel->leafs;
2000         endleaf = leaf + loadmodel->numleafs;
2001         for (;leaf < endleaf;leaf++)
2002         {
2003                 VectorSet(leaf->mins,  2000000000,  2000000000,  2000000000);
2004                 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2005         }
2006         p = portalchain;
2007         while(p)
2008         {
2009                 if (p->winding)
2010                 {
2011                         for (i = 0;i < 2;i++)
2012                         {
2013                                 leaf = (mleaf_t *)p->nodes[i];
2014                                 w = p->winding;
2015                                 for (j = 0;j < w->numpoints;j++)
2016                                 {
2017                                         if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2018                                         if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2019                                         if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2020                                         if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2021                                         if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2022                                         if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2023                                 }
2024                         }
2025                 }
2026                 p = p->chain;
2027         }
2028
2029         Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2030
2031         //Mem_CheckSentinelsGlobal();
2032
2033         // tally up portal and point counts
2034         p = portalchain;
2035         numportals = 0;
2036         numpoints = 0;
2037         while(p)
2038         {
2039                 // note: this check must match the one below or it will usually corrupt memory
2040                 // 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
2041                 if (p->winding && p->nodes[0] != p->nodes[1]
2042                  && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2043                  && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2044                 {
2045                         numportals += 2;
2046                         numpoints += p->winding->numpoints * 2;
2047                 }
2048                 p = p->chain;
2049         }
2050         loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2051         loadmodel->numportals = numportals;
2052         loadmodel->portalpoints = (void *) ((long) loadmodel->portals + numportals * sizeof(mportal_t));
2053         loadmodel->numportalpoints = numpoints;
2054         // clear all leaf portal chains
2055         for (i = 0;i < loadmodel->numleafs;i++)
2056                 loadmodel->leafs[i].portals = NULL;
2057         // process all portals in the global portal chain, while freeing them
2058         portal = loadmodel->portals;
2059         point = loadmodel->portalpoints;
2060         p = portalchain;
2061         portalchain = NULL;
2062         while (p)
2063         {
2064                 pnext = p->chain;
2065
2066                 if (p->winding)
2067                 {
2068                         // note: this check must match the one above or it will usually corrupt memory
2069                         // 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
2070                         if (p->nodes[0] != p->nodes[1]
2071                          && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2072                          && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2073                         {
2074                                 // first make the back to front portal (forward portal)
2075                                 portal->points = point;
2076                                 portal->numpoints = p->winding->numpoints;
2077                                 portal->plane.dist = p->plane.dist;
2078                                 VectorCopy(p->plane.normal, portal->plane.normal);
2079                                 portal->here = (mleaf_t *)p->nodes[1];
2080                                 portal->past = (mleaf_t *)p->nodes[0];
2081                                 // copy points
2082                                 for (j = 0;j < portal->numpoints;j++)
2083                                 {
2084                                         VectorCopy(p->winding->points[j], point->position);
2085                                         point++;
2086                                 }
2087                                 PlaneClassify(&portal->plane);
2088
2089                                 // link into leaf's portal chain
2090                                 portal->next = portal->here->portals;
2091                                 portal->here->portals = portal;
2092
2093                                 // advance to next portal
2094                                 portal++;
2095
2096                                 // then make the front to back portal (backward portal)
2097                                 portal->points = point;
2098                                 portal->numpoints = p->winding->numpoints;
2099                                 portal->plane.dist = -p->plane.dist;
2100                                 VectorNegate(p->plane.normal, portal->plane.normal);
2101                                 portal->here = (mleaf_t *)p->nodes[0];
2102                                 portal->past = (mleaf_t *)p->nodes[1];
2103                                 // copy points
2104                                 for (j = portal->numpoints - 1;j >= 0;j--)
2105                                 {
2106                                         VectorCopy(p->winding->points[j], point->position);
2107                                         point++;
2108                                 }
2109                                 PlaneClassify(&portal->plane);
2110
2111                                 // link into leaf's portal chain
2112                                 portal->next = portal->here->portals;
2113                                 portal->here->portals = portal;
2114
2115                                 // advance to next portal
2116                                 portal++;
2117                         }
2118                         FreeWinding(p->winding);
2119                 }
2120                 FreePortal(p);
2121                 p = pnext;
2122         }
2123
2124         //Mem_CheckSentinelsGlobal();
2125 }
2126
2127 /*
2128 =============
2129 AddPortalToNodes
2130 =============
2131 */
2132 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2133 {
2134         if (!front)
2135                 Host_Error ("AddPortalToNodes: NULL front node");
2136         if (!back)
2137                 Host_Error ("AddPortalToNodes: NULL back node");
2138         if (p->nodes[0] || p->nodes[1])
2139                 Host_Error ("AddPortalToNodes: already included");
2140         // 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
2141
2142         p->nodes[0] = front;
2143         p->next[0] = (portal_t *)front->portals;
2144         front->portals = (mportal_t *)p;
2145
2146         p->nodes[1] = back;
2147         p->next[1] = (portal_t *)back->portals;
2148         back->portals = (mportal_t *)p;
2149 }
2150
2151 /*
2152 =============
2153 RemovePortalFromNode
2154 =============
2155 */
2156 static void RemovePortalFromNodes(portal_t *portal)
2157 {
2158         int i;
2159         mnode_t *node;
2160         void **portalpointer;
2161         portal_t *t;
2162         for (i = 0;i < 2;i++)
2163         {
2164                 node = portal->nodes[i];
2165
2166                 portalpointer = (void **) &node->portals;
2167                 while (1)
2168                 {
2169                         t = *portalpointer;
2170                         if (!t)
2171                                 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2172
2173                         if (t == portal)
2174                         {
2175                                 if (portal->nodes[0] == node)
2176                                 {
2177                                         *portalpointer = portal->next[0];
2178                                         portal->nodes[0] = NULL;
2179                                 }
2180                                 else if (portal->nodes[1] == node)
2181                                 {
2182                                         *portalpointer = portal->next[1];
2183                                         portal->nodes[1] = NULL;
2184                                 }
2185                                 else
2186                                         Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2187                                 break;
2188                         }
2189
2190                         if (t->nodes[0] == node)
2191                                 portalpointer = (void **) &t->next[0];
2192                         else if (t->nodes[1] == node)
2193                                 portalpointer = (void **) &t->next[1];
2194                         else
2195                                 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2196                 }
2197         }
2198 }
2199
2200 static void Mod_RecursiveNodePortals (mnode_t *node)
2201 {
2202         int side;
2203         mnode_t *front, *back, *other_node;
2204         mplane_t clipplane, *plane;
2205         portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2206         winding_t *nodeportalwinding, *frontwinding, *backwinding;
2207
2208         //      CheckLeafPortalConsistancy (node);
2209
2210         // if a leaf, we're done
2211         if (node->contents)
2212                 return;
2213
2214         plane = node->plane;
2215
2216         front = node->children[0];
2217         back = node->children[1];
2218         if (front == back)
2219                 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2220
2221         // create the new portal by generating a polygon for the node plane,
2222         // and clipping it by all of the other portals (which came from nodes above this one)
2223         nodeportal = AllocPortal ();
2224         nodeportal->plane = *node->plane;
2225
2226         nodeportalwinding = BaseWindingForPlane (node->plane);
2227         //Mem_CheckSentinels(nodeportalwinding);
2228         side = 0;       // shut up compiler warning
2229         for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2230         {
2231                 clipplane = portal->plane;
2232                 if (portal->nodes[0] == portal->nodes[1])
2233                         Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2234                 if (portal->nodes[0] == node)
2235                         side = 0;
2236                 else if (portal->nodes[1] == node)
2237                 {
2238                         clipplane.dist = -clipplane.dist;
2239                         VectorNegate (clipplane.normal, clipplane.normal);
2240                         side = 1;
2241                 }
2242                 else
2243                         Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2244
2245                 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2246                 if (!nodeportalwinding)
2247                 {
2248                         printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2249                         break;
2250                 }
2251         }
2252
2253         if (nodeportalwinding)
2254         {
2255                 // if the plane was not clipped on all sides, there was an error
2256                 nodeportal->winding = nodeportalwinding;
2257                 AddPortalToNodes (nodeportal, front, back);
2258         }
2259
2260         // split the portals of this node along this node's plane and assign them to the children of this node
2261         // (migrating the portals downward through the tree)
2262         for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2263         {
2264                 if (portal->nodes[0] == portal->nodes[1])
2265                         Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2266                 if (portal->nodes[0] == node)
2267                         side = 0;
2268                 else if (portal->nodes[1] == node)
2269                         side = 1;
2270                 else
2271                         Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2272                 nextportal = portal->next[side];
2273
2274                 other_node = portal->nodes[!side];
2275                 RemovePortalFromNodes (portal);
2276
2277                 // cut the portal into two portals, one on each side of the node plane
2278                 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2279
2280                 if (!frontwinding)
2281                 {
2282                         if (side == 0)
2283                                 AddPortalToNodes (portal, back, other_node);
2284                         else
2285                                 AddPortalToNodes (portal, other_node, back);
2286                         continue;
2287                 }
2288                 if (!backwinding)
2289                 {
2290                         if (side == 0)
2291                                 AddPortalToNodes (portal, front, other_node);
2292                         else
2293                                 AddPortalToNodes (portal, other_node, front);
2294                         continue;
2295                 }
2296
2297                 // the winding is split
2298                 splitportal = AllocPortal ();
2299                 temp = splitportal->chain;
2300                 *splitportal = *portal;
2301                 splitportal->chain = temp;
2302                 splitportal->winding = backwinding;
2303                 FreeWinding (portal->winding);
2304                 portal->winding = frontwinding;
2305
2306                 if (side == 0)
2307                 {
2308                         AddPortalToNodes (portal, front, other_node);
2309                         AddPortalToNodes (splitportal, back, other_node);
2310                 }
2311                 else
2312                 {
2313                         AddPortalToNodes (portal, other_node, front);
2314                         AddPortalToNodes (splitportal, other_node, back);
2315                 }
2316         }
2317
2318         Mod_RecursiveNodePortals(front);
2319         Mod_RecursiveNodePortals(back);
2320 }
2321
2322 /*
2323 void Mod_MakeOutsidePortals(mnode_t *node)
2324 {
2325         int                     i, j;
2326         portal_t        *p, *portals[6];
2327         mnode_t         *outside_node;
2328
2329         outside_node = Mem_Alloc(loadmodel->mempool, sizeof(mnode_t));
2330         outside_node->contents = CONTENTS_SOLID;
2331         outside_node->portals = NULL;
2332
2333         for (i = 0;i < 3;i++)
2334         {
2335                 for (j = 0;j < 2;j++)
2336                 {
2337                         portals[j*3 + i] = p = AllocPortal ();
2338                         memset (&p->plane, 0, sizeof(mplane_t));
2339                         p->plane.normal[i] = j ? -1 : 1;
2340                         p->plane.dist = -65536;
2341                         p->winding = BaseWindingForPlane (&p->plane);
2342                         if (j)
2343                                 AddPortalToNodes (p, outside_node, node);
2344                         else
2345                                 AddPortalToNodes (p, node, outside_node);
2346                 }
2347         }
2348
2349         // clip the basewindings by all the other planes
2350         for (i = 0;i < 6;i++)
2351         {
2352                 for (j = 0;j < 6;j++)
2353                 {
2354                         if (j == i)
2355                                 continue;
2356                         portals[i]->winding = ClipWinding (portals[i]->winding, &portals[j]->plane, true);
2357                 }
2358         }
2359 }
2360 */
2361
2362 static void Mod_MakePortals(void)
2363 {
2364 //      Con_Printf("building portals for %s\n", loadmodel->name);
2365
2366         portalchain = NULL;
2367 //      Mod_MakeOutsidePortals (loadmodel->nodes);
2368         Mod_RecursiveNodePortals (loadmodel->nodes);
2369         Mod_FinalizePortals();
2370 }
2371
2372 /*
2373 =================
2374 Mod_LoadBrushModel
2375 =================
2376 */
2377 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2378 {
2379         int                     i, j;
2380         dheader_t       *header;
2381         dmodel_t        *bm;
2382         mempool_t       *mainmempool;
2383
2384         mod->type = mod_brush;
2385
2386         header = (dheader_t *)buffer;
2387
2388         i = LittleLong (header->version);
2389         if (i != BSPVERSION && i != 30)
2390                 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i or 30 (HalfLife))", mod->name, i, BSPVERSION);
2391         mod->ishlbsp = i == 30;
2392         if (loadmodel->isworldmodel)
2393                 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2394
2395 // swap all the lumps
2396         mod_base = (byte *)header;
2397
2398         for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
2399                 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
2400
2401 // load into heap
2402
2403         // store which lightmap format to use
2404         mod->lightmaprgba = r_lightmaprgba.integer;
2405
2406 //      Mem_CheckSentinelsGlobal();
2407         // LordHavoc: had to move entity loading above everything to allow parsing various settings from worldspawn
2408         Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2409 //      Mem_CheckSentinelsGlobal();
2410         Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
2411 //      Mem_CheckSentinelsGlobal();
2412         Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
2413 //      Mem_CheckSentinelsGlobal();
2414         Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
2415 //      Mem_CheckSentinelsGlobal();
2416         Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
2417 //      Mem_CheckSentinelsGlobal();
2418         Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
2419 //      Mem_CheckSentinelsGlobal();
2420         Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
2421 //      Mem_CheckSentinelsGlobal();
2422         Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
2423 //      Mem_CheckSentinelsGlobal();
2424         Mod_LoadFaces (&header->lumps[LUMP_FACES]);
2425 //      Mem_CheckSentinelsGlobal();
2426         Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
2427 //      Mem_CheckSentinelsGlobal();
2428         Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
2429 //      Mem_CheckSentinelsGlobal();
2430         Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
2431 //      Mem_CheckSentinelsGlobal();
2432         Mod_LoadNodes (&header->lumps[LUMP_NODES]);
2433 //      Mem_CheckSentinelsGlobal();
2434         Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
2435 //      Mem_CheckSentinelsGlobal();
2436 //      Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2437         Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
2438 //      Mem_CheckSentinelsGlobal();
2439
2440         Mod_MakeHull0 ();
2441 //      Mem_CheckSentinelsGlobal();
2442         Mod_MakePortals();
2443 //      Mem_CheckSentinelsGlobal();
2444
2445         mod->numframes = 2;             // regular and alternate animation
2446
2447         mainmempool = mod->mempool;
2448
2449 //
2450 // set up the submodels (FIXME: this is confusing)
2451 //
2452         for (i = 0;i < mod->numsubmodels;i++)
2453         {
2454                 int k, l;
2455                 float dist, modelyawradius, modelradius, *vec;
2456                 msurface_t *surf;
2457
2458                 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2459                 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2460                 modelyawradius = 0;
2461                 modelradius = 0;
2462
2463                 bm = &mod->submodels[i];
2464
2465                 mod->hulls[0].firstclipnode = bm->headnode[0];
2466                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2467                 {
2468                         mod->hulls[j].firstclipnode = bm->headnode[j];
2469                         mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2470                 }
2471
2472                 mod->firstmodelsurface = bm->firstface;
2473                 mod->nummodelsurfaces = bm->numfaces;
2474
2475                 mod->DrawSky = NULL;
2476                 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2477                 for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2478                 {
2479                         // we only need to have a drawsky function if it is used (usually only on world model)
2480                         if (surf->shader == &Cshader_sky)
2481                                 mod->DrawSky = R_DrawBrushModelSky;
2482                         for (k = 0;k < surf->numedges;k++)
2483                         {
2484                                 l = mod->surfedges[k + surf->firstedge];
2485                                 if (l > 0)
2486                                         vec = mod->vertexes[mod->edges[l].v[0]].position;
2487                                 else
2488                                         vec = mod->vertexes[mod->edges[-l].v[1]].position;
2489                                 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2490                                 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2491                                 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2492                                 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2493                                 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2494                                 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2495                                 dist = vec[0]*vec[0]+vec[1]*vec[1];
2496                                 if (modelyawradius < dist)
2497                                         modelyawradius = dist;
2498                                 dist += vec[2]*vec[2];
2499                                 if (modelradius < dist)
2500                                         modelradius = dist;
2501                         }
2502                 }
2503                 modelyawradius = sqrt(modelyawradius);
2504                 modelradius = sqrt(modelradius);
2505                 mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2506                 mod->yawmins[2] = mod->normalmins[2];
2507                 mod->yawmaxs[2] = mod->normalmaxs[2];
2508                 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2509                 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2510 //              mod->modelradius = modelradius;
2511
2512 //              VectorCopy (bm->maxs, mod->maxs);
2513 //              VectorCopy (bm->mins, mod->mins);
2514
2515 //              mod->radius = RadiusFromBounds (mod->mins, mod->maxs);
2516
2517                 mod->numleafs = bm->visleafs;
2518
2519                 mod->SERAddEntity = Mod_Brush_SERAddEntity;
2520                 mod->Draw = R_DrawBrushModelNormal;
2521                 mod->DrawShadow = NULL;
2522
2523                 Mod_BrushSortedSurfaces(mod, mainmempool);
2524
2525                 // LordHavoc: only register submodels if it is the world
2526                 // (prevents bsp models from replacing world submodels)
2527                 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2528                 {
2529                         char    name[10];
2530                         // duplicate the basic information
2531                         sprintf (name, "*%i", i+1);
2532                         loadmodel = Mod_FindName (name);
2533                         *loadmodel = *mod;
2534                         strcpy (loadmodel->name, name);
2535                         // textures and memory belong to the main model
2536                         loadmodel->texturepool = NULL;
2537                         loadmodel->mempool = NULL;
2538                         mod = loadmodel;
2539                 }
2540         }
2541 //      Mem_CheckSentinelsGlobal();
2542 }