]> icculus.org git repositories - divverent/darkplaces.git/blob - model_brush.c
knight/scrag/vore trail effects are half as intense now
[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 qbyte 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 qbyte *Mod_DecompressVis (qbyte *in, model_t *model)
122 {
123         static qbyte decompressed[MAX_MAP_LEAFS/8];
124         int c;
125         qbyte *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 qbyte *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         qbyte 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         qbyte                   *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 *)((qbyte *)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 = (qbyte *)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         qbyte *in, *out, *data, d;
490         char litfilename[1024];
491         loadmodel->lightdata = NULL;
492         if (loadmodel->ishlbsp) // LordHavoc: load the colored lighting data straight
493         {
494                 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
495                 memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
496         }
497         else // LordHavoc: bsp version 29 (normal white lighting)
498         {
499                 // LordHavoc: hope is not lost yet, check for a .lit file to load
500                 strcpy(litfilename, loadmodel->name);
501                 COM_StripExtension(litfilename, litfilename);
502                 strcat(litfilename, ".lit");
503                 data = (qbyte*) COM_LoadFile (litfilename, false);
504                 if (data)
505                 {
506                         if (loadsize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
507                         {
508                                 i = LittleLong(((int *)data)[1]);
509                                 if (i == 1)
510                                 {
511                                         Con_DPrintf("%s loaded", litfilename);
512                                         loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, loadsize - 8);
513                                         memcpy(loadmodel->lightdata, data + 8, loadsize - 8);
514                                         Mem_Free(data);
515                                         return;
516                                 }
517                                 else
518                                 {
519                                         Con_Printf("Unknown .lit file version (%d)\n", i);
520                                         Mem_Free(data);
521                                 }
522                         }
523                         else
524                         {
525                                 if (loadsize == 8)
526                                         Con_Printf("Empty .lit file, ignoring\n");
527                                 else
528                                         Con_Printf("Corrupt .lit file (old version?), ignoring\n");
529                                 Mem_Free(data);
530                         }
531                 }
532                 // LordHavoc: oh well, expand the white lighting data
533                 if (!l->filelen)
534                         return;
535                 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
536                 in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
537                 out = loadmodel->lightdata;
538                 memcpy (in, mod_base + l->fileofs, l->filelen);
539                 for (i = 0;i < l->filelen;i++)
540                 {
541                         d = *in++;
542                         *out++ = d;
543                         *out++ = d;
544                         *out++ = d;
545                 }
546         }
547 }
548
549
550 /*
551 =================
552 Mod_LoadVisibility
553 =================
554 */
555 static void Mod_LoadVisibility (lump_t *l)
556 {
557         if (!l->filelen)
558         {
559                 loadmodel->visdata = NULL;
560                 return;
561         }
562         loadmodel->visdata = Mem_Alloc(loadmodel->mempool, l->filelen);
563         memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
564 }
565
566 // used only for HalfLife maps
567 void Mod_ParseWadsFromEntityLump(char *data)
568 {
569         char key[128], value[4096];
570         char wadname[128];
571         int i, j, k;
572         if (!data)
573                 return;
574         data = COM_Parse(data);
575         if (!data)
576                 return; // error
577         if (com_token[0] != '{')
578                 return; // error
579         while (1)
580         {
581                 data = COM_Parse(data);
582                 if (!data)
583                         return; // error
584                 if (com_token[0] == '}')
585                         break; // end of worldspawn
586                 if (com_token[0] == '_')
587                         strcpy(key, com_token + 1);
588                 else
589                         strcpy(key, com_token);
590                 while (key[strlen(key)-1] == ' ') // remove trailing spaces
591                         key[strlen(key)-1] = 0;
592                 data = COM_Parse(data);
593                 if (!data)
594                         return; // error
595                 strcpy(value, com_token);
596                 if (!strcmp("wad", key)) // for HalfLife maps
597                 {
598                         if (loadmodel->ishlbsp)
599                         {
600                                 j = 0;
601                                 for (i = 0;i < 4096;i++)
602                                         if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
603                                                 break;
604                                 if (value[i])
605                                 {
606                                         for (;i < 4096;i++)
607                                         {
608                                                 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
609                                                 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
610                                                         j = i+1;
611                                                 else if (value[i] == ';' || value[i] == 0)
612                                                 {
613                                                         k = value[i];
614                                                         value[i] = 0;
615                                                         strcpy(wadname, "textures/");
616                                                         strcat(wadname, &value[j]);
617                                                         W_LoadTextureWadFile (wadname, false);
618                                                         j = i+1;
619                                                         if (!k)
620                                                                 break;
621                                                 }
622                                         }
623                                 }
624                         }
625                 }
626         }
627 }
628
629 /*
630 =================
631 Mod_LoadEntities
632 =================
633 */
634 static void Mod_LoadEntities (lump_t *l)
635 {
636         if (!l->filelen)
637         {
638                 loadmodel->entities = NULL;
639                 return;
640         }
641         loadmodel->entities = Mem_Alloc(loadmodel->mempool, l->filelen);
642         memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
643         if (loadmodel->ishlbsp)
644                 Mod_ParseWadsFromEntityLump(loadmodel->entities);
645 }
646
647
648 /*
649 =================
650 Mod_LoadVertexes
651 =================
652 */
653 static void Mod_LoadVertexes (lump_t *l)
654 {
655         dvertex_t       *in;
656         mvertex_t       *out;
657         int                     i, count;
658
659         in = (void *)(mod_base + l->fileofs);
660         if (l->filelen % sizeof(*in))
661                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
662         count = l->filelen / sizeof(*in);
663         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
664
665         loadmodel->vertexes = out;
666         loadmodel->numvertexes = count;
667
668         for ( i=0 ; i<count ; i++, in++, out++)
669         {
670                 out->position[0] = LittleFloat (in->point[0]);
671                 out->position[1] = LittleFloat (in->point[1]);
672                 out->position[2] = LittleFloat (in->point[2]);
673         }
674 }
675
676 /*
677 =================
678 Mod_LoadSubmodels
679 =================
680 */
681 static void Mod_LoadSubmodels (lump_t *l)
682 {
683         dmodel_t        *in;
684         dmodel_t        *out;
685         int                     i, j, count;
686
687         in = (void *)(mod_base + l->fileofs);
688         if (l->filelen % sizeof(*in))
689                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
690         count = l->filelen / sizeof(*in);
691         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
692
693         loadmodel->submodels = out;
694         loadmodel->numsubmodels = count;
695
696         for ( i=0 ; i<count ; i++, in++, out++)
697         {
698                 for (j=0 ; j<3 ; j++)
699                 {
700                         // spread the mins / maxs by a pixel
701                         out->mins[j] = LittleFloat (in->mins[j]) - 1;
702                         out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
703                         out->origin[j] = LittleFloat (in->origin[j]);
704                 }
705                 for (j=0 ; j<MAX_MAP_HULLS ; j++)
706                         out->headnode[j] = LittleLong (in->headnode[j]);
707                 out->visleafs = LittleLong (in->visleafs);
708                 out->firstface = LittleLong (in->firstface);
709                 out->numfaces = LittleLong (in->numfaces);
710         }
711 }
712
713 /*
714 =================
715 Mod_LoadEdges
716 =================
717 */
718 static void Mod_LoadEdges (lump_t *l)
719 {
720         dedge_t *in;
721         medge_t *out;
722         int     i, count;
723
724         in = (void *)(mod_base + l->fileofs);
725         if (l->filelen % sizeof(*in))
726                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
727         count = l->filelen / sizeof(*in);
728         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
729
730         loadmodel->edges = out;
731         loadmodel->numedges = count;
732
733         for ( i=0 ; i<count ; i++, in++, out++)
734         {
735                 out->v[0] = (unsigned short)LittleShort(in->v[0]);
736                 out->v[1] = (unsigned short)LittleShort(in->v[1]);
737         }
738 }
739
740 /*
741 =================
742 Mod_LoadTexinfo
743 =================
744 */
745 static void Mod_LoadTexinfo (lump_t *l)
746 {
747         texinfo_t *in;
748         mtexinfo_t *out;
749         int     i, j, k, count;
750         int             miptex;
751
752         in = (void *)(mod_base + l->fileofs);
753         if (l->filelen % sizeof(*in))
754                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
755         count = l->filelen / sizeof(*in);
756         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
757
758         loadmodel->texinfo = out;
759         loadmodel->numtexinfo = count;
760
761         for (i = 0;i < count;i++, in++, out++)
762         {
763                 for (k = 0;k < 2;k++)
764                         for (j = 0;j < 4;j++)
765                                 out->vecs[k][j] = LittleFloat (in->vecs[k][j]);
766
767                 miptex = LittleLong (in->miptex);
768                 out->flags = LittleLong (in->flags);
769
770                 if (!loadmodel->textures)
771                         out->texture = &loadmodel->notexture;
772                 else
773                 {
774                         if (miptex < 0)
775                                 Host_Error ("miptex < 0");
776                         if (miptex >= loadmodel->numtextures)
777                                 Host_Error ("miptex >= loadmodel->numtextures");
778                         out->texture = loadmodel->textures[miptex];
779                 }
780                 if (!out->texture)
781                         out->texture = &loadmodel->notexture;
782         }
783 }
784
785 /*
786 ================
787 CalcSurfaceExtents
788
789 Fills in s->texturemins[] and s->extents[]
790 ================
791 */
792 static void CalcSurfaceExtents (msurface_t *s)
793 {
794         float   mins[2], maxs[2], val;
795         int             i,j, e;
796         mvertex_t       *v;
797         mtexinfo_t      *tex;
798         int             bmins[2], bmaxs[2];
799
800         mins[0] = mins[1] = 999999999;
801         maxs[0] = maxs[1] = -999999999;
802
803         tex = s->texinfo;
804
805         for (i=0 ; i<s->numedges ; i++)
806         {
807                 e = loadmodel->surfedges[s->firstedge+i];
808                 if (e >= 0)
809                         v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
810                 else
811                         v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
812
813                 for (j=0 ; j<2 ; j++)
814                 {
815                         val = v->position[0] * tex->vecs[j][0] +
816                                 v->position[1] * tex->vecs[j][1] +
817                                 v->position[2] * tex->vecs[j][2] +
818                                 tex->vecs[j][3];
819                         if (val < mins[j])
820                                 mins[j] = val;
821                         if (val > maxs[j])
822                                 maxs[j] = val;
823                 }
824         }
825
826         for (i=0 ; i<2 ; i++)
827         {
828                 bmins[i] = floor(mins[i]/16);
829                 bmaxs[i] = ceil(maxs[i]/16);
830
831                 s->texturemins[i] = bmins[i] * 16;
832                 s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
833         }
834 }
835
836
837 void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs)
838 {
839         int             i, j;
840         float   *v;
841
842         mins[0] = mins[1] = mins[2] = 9999;
843         maxs[0] = maxs[1] = maxs[2] = -9999;
844         v = verts;
845         for (i = 0;i < numverts;i++)
846         {
847                 for (j = 0;j < 3;j++, v++)
848                 {
849                         if (*v < mins[j])
850                                 mins[j] = *v;
851                         if (*v > maxs[j])
852                                 maxs[j] = *v;
853                 }
854         }
855 }
856
857 #define MAX_SUBDIVPOLYTRIANGLES 4096
858 #define MAX_SUBDIVPOLYVERTS (MAX_SUBDIVPOLYTRIANGLES * 3)
859
860 static int subdivpolyverts, subdivpolytriangles;
861 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
862 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
863
864 static int subdivpolylookupvert(vec3_t v)
865 {
866         int i;
867         for (i = 0;i < subdivpolyverts;i++)
868                 if (subdivpolyvert[i][0] == v[0]
869                  && subdivpolyvert[i][1] == v[1]
870                  && subdivpolyvert[i][2] == v[2])
871                         return i;
872         if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
873                 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
874         VectorCopy(v, subdivpolyvert[subdivpolyverts]);
875         return subdivpolyverts++;
876 }
877
878 static void SubdividePolygon (int numverts, float *verts)
879 {
880         int             i, i1, i2, i3, f, b, c, p;
881         vec3_t  mins, maxs, front[256], back[256];
882         float   m, *pv, *cv, dist[256], frac;
883
884         if (numverts > 250)
885                 Host_Error ("SubdividePolygon: ran out of verts in buffer");
886
887         BoundPoly (numverts, verts, mins, maxs);
888
889         for (i = 0;i < 3;i++)
890         {
891                 m = (mins[i] + maxs[i]) * 0.5;
892                 m = r_subdivide_size.value * floor (m/r_subdivide_size.value + 0.5);
893                 if (maxs[i] - m < 8)
894                         continue;
895                 if (m - mins[i] < 8)
896                         continue;
897
898                 // cut it
899                 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
900                         dist[c] = cv[i] - m;
901
902                 f = b = 0;
903                 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
904                 {
905                         if (dist[p] >= 0)
906                         {
907                                 VectorCopy (pv, front[f]);
908                                 f++;
909                         }
910                         if (dist[p] <= 0)
911                         {
912                                 VectorCopy (pv, back[b]);
913                                 b++;
914                         }
915                         if (dist[p] == 0 || dist[c] == 0)
916                                 continue;
917                         if ( (dist[p] > 0) != (dist[c] > 0) )
918                         {
919                                 // clip point
920                                 frac = dist[p] / (dist[p] - dist[c]);
921                                 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
922                                 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
923                                 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
924                                 f++;
925                                 b++;
926                         }
927                 }
928
929                 SubdividePolygon (f, front[0]);
930                 SubdividePolygon (b, back[0]);
931                 return;
932         }
933
934         i1 = subdivpolylookupvert(verts);
935         i2 = subdivpolylookupvert(verts + 3);
936         for (i = 2;i < numverts;i++)
937         {
938                 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
939                 {
940                         Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
941                         return;
942                 }
943
944                 i3 = subdivpolylookupvert(verts + i * 3);
945                 subdivpolyindex[subdivpolytriangles][0] = i1;
946                 subdivpolyindex[subdivpolytriangles][1] = i2;
947                 subdivpolyindex[subdivpolytriangles][2] = i3;
948                 i2 = i3;
949                 subdivpolytriangles++;
950         }
951 }
952
953 /*
954 ================
955 Mod_GenerateWarpMesh
956
957 Breaks a polygon up along axial 64 unit
958 boundaries so that turbulent and sky warps
959 can be done reasonably.
960 ================
961 */
962 void Mod_GenerateWarpMesh (msurface_t *surf)
963 {
964         int                             i, j;
965         surfvertex_t    *v;
966         surfmesh_t              *mesh;
967
968         subdivpolytriangles = 0;
969         subdivpolyverts = 0;
970         SubdividePolygon (surf->poly_numverts, surf->poly_verts);
971
972         mesh = &surf->mesh;
973         mesh->numverts = subdivpolyverts;
974         mesh->numtriangles = subdivpolytriangles;
975         if (mesh->numtriangles < 1)
976                 Host_Error("Mod_GenerateWarpMesh: no triangles?\n");
977         mesh->index = Mem_Alloc(loadmodel->mempool, mesh->numtriangles * sizeof(int[3]) + mesh->numverts * sizeof(surfvertex_t));
978         mesh->vertex = (surfvertex_t *)((long) mesh->index + mesh->numtriangles * sizeof(int[3]));
979         memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
980
981         for (i = 0;i < mesh->numtriangles;i++)
982         {
983                 for (j = 0;j < 3;j++)
984                 {
985                         mesh->index[i*3+j] = subdivpolyindex[i][j];
986                         //if (mesh->index[i] < 0 || mesh->index[i] >= mesh->numverts)
987                         //      Host_Error("Mod_GenerateWarpMesh: invalid index generated\n");
988                 }
989         }
990
991         for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
992         {
993                 VectorCopy(subdivpolyvert[i], v->v);
994                 v->st[0] = DotProduct (v->v, surf->texinfo->vecs[0]);
995                 v->st[1] = DotProduct (v->v, surf->texinfo->vecs[1]);
996         }
997 }
998
999 void Mod_GenerateVertexLitMesh (msurface_t *surf)
1000 {
1001         int                             i, is, it, *index, smax, tmax;
1002         float                   *in, s, t;
1003         surfvertex_t    *out;
1004         surfmesh_t              *mesh;
1005
1006         //surf->flags |= SURF_LIGHTMAP;
1007         smax = surf->extents[0] >> 4;
1008         tmax = surf->extents[1] >> 4;
1009         surf->lightmaptexturestride = 0;
1010         surf->lightmaptexture = NULL;
1011
1012         mesh = &surf->mesh;
1013         mesh->numverts = surf->poly_numverts;
1014         mesh->numtriangles = surf->poly_numverts - 2;
1015         mesh->index = Mem_Alloc(loadmodel->mempool, mesh->numtriangles * sizeof(int[3]) + mesh->numverts * sizeof(surfvertex_t));
1016         mesh->vertex = (surfvertex_t *)((long) mesh->index + mesh->numtriangles * sizeof(int[3]));
1017         memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1018
1019         index = mesh->index;
1020         for (i = 0;i < mesh->numtriangles;i++)
1021         {
1022                 *index++ = 0;
1023                 *index++ = i + 1;
1024                 *index++ = i + 2;
1025         }
1026
1027         for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1028         {
1029                 VectorCopy (in, out->v);
1030
1031                 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1032                 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1033
1034                 out->st[0] = s / surf->texinfo->texture->width;
1035                 out->st[1] = t / surf->texinfo->texture->height;
1036
1037                 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1038                 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1039
1040                 // lightmap coordinates
1041                 out->uv[0] = 0;
1042                 out->uv[1] = 0;
1043
1044                 // LordHavoc: calc lightmap data offset for vertex lighting to use
1045                 is = (int) s;
1046                 it = (int) t;
1047                 is = bound(0, is, smax);
1048                 it = bound(0, it, tmax);
1049                 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1050         }
1051 }
1052
1053 void Mod_GenerateLightmappedMesh (msurface_t *surf)
1054 {
1055         int                             i, is, it, *index, smax, tmax;
1056         float                   *in, s, t, xbase, ybase, xscale, yscale;
1057         surfvertex_t    *out;
1058         surfmesh_t              *mesh;
1059
1060         surf->flags |= SURF_LIGHTMAP;
1061         smax = surf->extents[0] >> 4;
1062         tmax = surf->extents[1] >> 4;
1063         if (r_miplightmaps.integer)
1064         {
1065                 surf->lightmaptexturestride = (surf->extents[0]>>4)+1;
1066                 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);
1067         }
1068         else
1069         {
1070                 surf->lightmaptexturestride = R_CompatibleFragmentWidth((surf->extents[0]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1071                 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);
1072         }
1073 //      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);
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_PRECACHE);
1075         R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &xbase, &ybase, &xscale, &yscale);
1076         xscale = (xscale - xbase) * 16.0 / ((surf->extents[0] & ~15) + 16);
1077         yscale = (yscale - ybase) * 16.0 / ((surf->extents[1] & ~15) + 16);
1078
1079         mesh = &surf->mesh;
1080         mesh->numverts = surf->poly_numverts;
1081         mesh->numtriangles = surf->poly_numverts - 2;
1082         mesh->index = Mem_Alloc(loadmodel->mempool, mesh->numtriangles * sizeof(int[3]) + mesh->numverts * sizeof(surfvertex_t));
1083         mesh->vertex = (surfvertex_t *)((long) mesh->index + mesh->numtriangles * sizeof(int[3]));
1084         memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1085
1086         index = mesh->index;
1087         for (i = 0;i < mesh->numtriangles;i++)
1088         {
1089                 *index++ = 0;
1090                 *index++ = i + 1;
1091                 *index++ = i + 2;
1092         }
1093
1094         for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1095         {
1096                 VectorCopy (in, out->v);
1097
1098                 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1099                 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1100
1101                 out->st[0] = s / surf->texinfo->texture->width;
1102                 out->st[1] = t / surf->texinfo->texture->height;
1103
1104                 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1105                 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1106
1107                 // lightmap coordinates
1108                 out->uv[0] = s * xscale + xbase;
1109                 out->uv[1] = t * yscale + ybase;
1110
1111                 // LordHavoc: calc lightmap data offset for vertex lighting to use
1112                 is = (int) s;
1113                 it = (int) t;
1114                 is = bound(0, is, smax);
1115                 it = bound(0, it, tmax);
1116                 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1117         }
1118 }
1119
1120 void Mod_GenerateVertexMesh (msurface_t *surf)
1121 {
1122         int                             i, *index;
1123         float                   *in;
1124         surfvertex_t    *out;
1125         surfmesh_t              *mesh;
1126
1127         surf->lightmaptexturestride = 0;
1128         surf->lightmaptexture = NULL;
1129
1130         mesh = &surf->mesh;
1131         mesh->numverts = surf->poly_numverts;
1132         mesh->numtriangles = surf->poly_numverts - 2;
1133         mesh->index = Mem_Alloc(loadmodel->mempool, mesh->numtriangles * sizeof(int[3]) + mesh->numverts * sizeof(surfvertex_t));
1134         mesh->vertex = (surfvertex_t *)((long) mesh->index + mesh->numtriangles * sizeof(int[3]));
1135         memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1136
1137         index = mesh->index;
1138         for (i = 0;i < mesh->numtriangles;i++)
1139         {
1140                 *index++ = 0;
1141                 *index++ = i + 1;
1142                 *index++ = i + 2;
1143         }
1144
1145         for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1146         {
1147                 VectorCopy (in, out->v);
1148                 out->st[0] = (DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) / surf->texinfo->texture->width;
1149                 out->st[1] = (DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) / surf->texinfo->texture->height;
1150         }
1151 }
1152
1153 void Mod_GenerateSurfacePolygon (msurface_t *surf)
1154 {
1155         float           *vert;
1156         int                     i;
1157         int                     lindex;
1158         float           *vec;
1159
1160         // convert edges back to a normal polygon
1161         surf->poly_numverts = surf->numedges;
1162         vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges);
1163         for (i = 0;i < surf->numedges;i++)
1164         {
1165                 lindex = loadmodel->surfedges[surf->firstedge + i];
1166                 if (lindex > 0)
1167                         vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
1168                 else
1169                         vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
1170                 VectorCopy (vec, vert);
1171                 vert += 3;
1172         }
1173 }
1174
1175 /*
1176 =================
1177 Mod_LoadFaces
1178 =================
1179 */
1180 static void Mod_LoadFaces (lump_t *l)
1181 {
1182         dface_t         *in;
1183         msurface_t      *out;
1184         int                     i, count, surfnum, planenum, side, ssize, tsize;
1185
1186         in = (void *)(mod_base + l->fileofs);
1187         if (l->filelen % sizeof(*in))
1188                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1189         count = l->filelen / sizeof(*in);
1190         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1191
1192         loadmodel->surfaces = out;
1193         loadmodel->numsurfaces = count;
1194
1195         for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
1196         {
1197                 // FIXME: validate edges, texinfo, etc?
1198                 out->firstedge = LittleLong(in->firstedge);
1199                 out->numedges = LittleShort(in->numedges);
1200
1201                 out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo);
1202                 out->flags = out->texinfo->texture->flags;
1203
1204                 planenum = LittleShort(in->planenum);
1205                 side = LittleShort(in->side);
1206                 if (side)
1207                         out->flags |= SURF_PLANEBACK;
1208
1209                 out->plane = loadmodel->planes + planenum;
1210
1211                 // clear lightmap (filled in later)
1212                 out->lightmaptexture = NULL;
1213
1214                 // force lightmap upload on first time seeing the surface
1215                 out->cached_dlight = true;
1216                 out->cached_ambient = -1000;
1217                 out->cached_lightscalebit = -1000;
1218
1219                 CalcSurfaceExtents (out);
1220
1221                 ssize = (out->extents[0] >> 4) + 1;
1222                 tsize = (out->extents[1] >> 4) + 1;
1223
1224                 // lighting info
1225                 for (i = 0;i < MAXLIGHTMAPS;i++)
1226                         out->styles[i] = in->styles[i];
1227                 i = LittleLong(in->lightofs);
1228                 if (i == -1)
1229                         out->samples = NULL;
1230                 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1231                         out->samples = loadmodel->lightdata + i;
1232                 else // LordHavoc: white lighting (bsp version 29)
1233                         out->samples = loadmodel->lightdata + (i * 3);
1234
1235                 Mod_GenerateSurfacePolygon(out);
1236
1237                 if (out->texinfo->texture->flags & SURF_DRAWSKY)
1238                 {
1239                         out->shader = &Cshader_sky;
1240                         out->samples = NULL;
1241                         Mod_GenerateWarpMesh (out);
1242                         continue;
1243                 }
1244
1245                 if (out->texinfo->texture->flags & SURF_DRAWTURB)
1246                 {
1247                         out->shader = &Cshader_water;
1248                         /*
1249                         for (i=0 ; i<2 ; i++)
1250                         {
1251                                 out->extents[i] = 16384*1024;
1252                                 out->texturemins[i] = -8192*1024;
1253                         }
1254                         */
1255                         out->samples = NULL;
1256                         Mod_GenerateWarpMesh (out);
1257                         continue;
1258                 }
1259
1260                 if (!R_TextureHasAlpha(out->texinfo->texture->texture))
1261                         out->flags |= SURF_CLIPSOLID;
1262                 if (out->texinfo->flags & TEX_SPECIAL)
1263                 {
1264                         // qbsp couldn't find the texture for this surface, but it was either turb or sky...  assume turb
1265                         out->shader = &Cshader_water;
1266                         out->samples = NULL;
1267                         Mod_GenerateWarpMesh (out);
1268                 }
1269                 else if ((out->extents[0]+1) > (256*16) || (out->extents[1]+1) > (256*16))
1270                 {
1271                         Con_Printf ("Bad surface extents, converting to fullbright polygon");
1272                         out->shader = &Cshader_wall_fullbright;
1273                         out->samples = NULL;
1274                         Mod_GenerateVertexMesh(out);
1275                 }
1276                 else
1277                 {
1278                         // stainmap for permanent marks on walls
1279                         out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1280                         // clear to white
1281                         memset(out->stainsamples, 255, ssize * tsize * 3);
1282                         if (out->extents[0] < r_vertexsurfacesthreshold.integer && out->extents[1] < r_vertexsurfacesthreshold.integer)
1283                         {
1284                                 out->shader = &Cshader_wall_vertex;
1285                                 Mod_GenerateVertexLitMesh(out);
1286                         }
1287                         else
1288                         {
1289                                 out->shader = &Cshader_wall_lightmap;
1290                                 Mod_GenerateLightmappedMesh(out);
1291                         }
1292                 }
1293         }
1294 }
1295
1296 static model_t *sortmodel;
1297
1298 static int Mod_SurfaceQSortCompare(const void *voida, const void *voidb)
1299 {
1300         const msurface_t *a, *b;
1301         a = *((const msurface_t **)voida);
1302         b = *((const msurface_t **)voidb);
1303         if (a->shader != b->shader)
1304                 return (long) a->shader - (long) b->shader;
1305         if (a->texinfo->texture != b->texinfo->texture);
1306                 return a->texinfo->texture - b->texinfo->texture;
1307         return 0;
1308 }
1309
1310 static void Mod_BrushSortedSurfaces(model_t *model, mempool_t *pool)
1311 {
1312         int surfnum;
1313         sortmodel = model;
1314         sortmodel->modelsortedsurfaces = Mem_Alloc(pool, sortmodel->nummodelsurfaces * sizeof(msurface_t *));
1315         for (surfnum = 0;surfnum < sortmodel->nummodelsurfaces;surfnum++)
1316                 sortmodel->modelsortedsurfaces[surfnum] = &sortmodel->surfaces[surfnum + sortmodel->firstmodelsurface];
1317
1318         qsort(sortmodel->modelsortedsurfaces, sortmodel->nummodelsurfaces, sizeof(msurface_t *), Mod_SurfaceQSortCompare);
1319 }
1320
1321
1322 /*
1323 =================
1324 Mod_SetParent
1325 =================
1326 */
1327 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1328 {
1329         node->parent = parent;
1330         if (node->contents < 0)
1331                 return;
1332         Mod_SetParent (node->children[0], node);
1333         Mod_SetParent (node->children[1], node);
1334 }
1335
1336 /*
1337 =================
1338 Mod_LoadNodes
1339 =================
1340 */
1341 static void Mod_LoadNodes (lump_t *l)
1342 {
1343         int                     i, j, count, p;
1344         dnode_t         *in;
1345         mnode_t         *out;
1346
1347         in = (void *)(mod_base + l->fileofs);
1348         if (l->filelen % sizeof(*in))
1349                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1350         count = l->filelen / sizeof(*in);
1351         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1352
1353         loadmodel->nodes = out;
1354         loadmodel->numnodes = count;
1355
1356         for ( i=0 ; i<count ; i++, in++, out++)
1357         {
1358                 for (j=0 ; j<3 ; j++)
1359                 {
1360                         out->mins[j] = LittleShort (in->mins[j]);
1361                         out->maxs[j] = LittleShort (in->maxs[j]);
1362                 }
1363
1364                 p = LittleLong(in->planenum);
1365                 out->plane = loadmodel->planes + p;
1366
1367                 out->firstsurface = LittleShort (in->firstface);
1368                 out->numsurfaces = LittleShort (in->numfaces);
1369
1370                 for (j=0 ; j<2 ; j++)
1371                 {
1372                         p = LittleShort (in->children[j]);
1373                         if (p >= 0)
1374                                 out->children[j] = loadmodel->nodes + p;
1375                         else
1376                                 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1377                 }
1378         }
1379
1380         Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1381 }
1382
1383 /*
1384 =================
1385 Mod_LoadLeafs
1386 =================
1387 */
1388 static void Mod_LoadLeafs (lump_t *l)
1389 {
1390         dleaf_t         *in;
1391         mleaf_t         *out;
1392         int                     i, j, count, p;
1393
1394         in = (void *)(mod_base + l->fileofs);
1395         if (l->filelen % sizeof(*in))
1396                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1397         count = l->filelen / sizeof(*in);
1398         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1399
1400         loadmodel->leafs = out;
1401         loadmodel->numleafs = count;
1402
1403         for ( i=0 ; i<count ; i++, in++, out++)
1404         {
1405                 for (j=0 ; j<3 ; j++)
1406                 {
1407                         out->mins[j] = LittleShort (in->mins[j]);
1408                         out->maxs[j] = LittleShort (in->maxs[j]);
1409                 }
1410
1411                 p = LittleLong(in->contents);
1412                 out->contents = p;
1413
1414                 out->firstmarksurface = loadmodel->marksurfaces +
1415                         LittleShort(in->firstmarksurface);
1416                 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1417
1418                 p = LittleLong(in->visofs);
1419                 if (p == -1)
1420                         out->compressed_vis = NULL;
1421                 else
1422                         out->compressed_vis = loadmodel->visdata + p;
1423
1424                 for (j=0 ; j<4 ; j++)
1425                         out->ambient_sound_level[j] = in->ambient_level[j];
1426
1427                 // gl underwater warp
1428                 // LordHavoc: disabled underwater warping
1429                 /*
1430                 if (out->contents != CONTENTS_EMPTY)
1431                 {
1432                         for (j=0 ; j<out->nummarksurfaces ; j++)
1433                                 out->firstmarksurface[j]->flags |= SURF_UNDERWATER;
1434                 }
1435                 */
1436         }
1437 }
1438
1439 /*
1440 =================
1441 Mod_LoadClipnodes
1442 =================
1443 */
1444 static void Mod_LoadClipnodes (lump_t *l)
1445 {
1446         dclipnode_t *in, *out;
1447         int                     i, count;
1448         hull_t          *hull;
1449
1450         in = (void *)(mod_base + l->fileofs);
1451         if (l->filelen % sizeof(*in))
1452                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1453         count = l->filelen / sizeof(*in);
1454         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1455
1456         loadmodel->clipnodes = out;
1457         loadmodel->numclipnodes = count;
1458
1459         if (loadmodel->ishlbsp)
1460         {
1461                 hull = &loadmodel->hulls[1];
1462                 hull->clipnodes = out;
1463                 hull->firstclipnode = 0;
1464                 hull->lastclipnode = count-1;
1465                 hull->planes = loadmodel->planes;
1466                 hull->clip_mins[0] = -16;
1467                 hull->clip_mins[1] = -16;
1468                 hull->clip_mins[2] = -36;
1469                 hull->clip_maxs[0] = 16;
1470                 hull->clip_maxs[1] = 16;
1471                 hull->clip_maxs[2] = 36;
1472
1473                 hull = &loadmodel->hulls[2];
1474                 hull->clipnodes = out;
1475                 hull->firstclipnode = 0;
1476                 hull->lastclipnode = count-1;
1477                 hull->planes = loadmodel->planes;
1478                 hull->clip_mins[0] = -32;
1479                 hull->clip_mins[1] = -32;
1480                 hull->clip_mins[2] = -32;
1481                 hull->clip_maxs[0] = 32;
1482                 hull->clip_maxs[1] = 32;
1483                 hull->clip_maxs[2] = 32;
1484
1485                 hull = &loadmodel->hulls[3];
1486                 hull->clipnodes = out;
1487                 hull->firstclipnode = 0;
1488                 hull->lastclipnode = count-1;
1489                 hull->planes = loadmodel->planes;
1490                 hull->clip_mins[0] = -16;
1491                 hull->clip_mins[1] = -16;
1492                 hull->clip_mins[2] = -18;
1493                 hull->clip_maxs[0] = 16;
1494                 hull->clip_maxs[1] = 16;
1495                 hull->clip_maxs[2] = 18;
1496         }
1497         else
1498         {
1499                 hull = &loadmodel->hulls[1];
1500                 hull->clipnodes = out;
1501                 hull->firstclipnode = 0;
1502                 hull->lastclipnode = count-1;
1503                 hull->planes = loadmodel->planes;
1504                 hull->clip_mins[0] = -16;
1505                 hull->clip_mins[1] = -16;
1506                 hull->clip_mins[2] = -24;
1507                 hull->clip_maxs[0] = 16;
1508                 hull->clip_maxs[1] = 16;
1509                 hull->clip_maxs[2] = 32;
1510
1511                 hull = &loadmodel->hulls[2];
1512                 hull->clipnodes = out;
1513                 hull->firstclipnode = 0;
1514                 hull->lastclipnode = count-1;
1515                 hull->planes = loadmodel->planes;
1516                 hull->clip_mins[0] = -32;
1517                 hull->clip_mins[1] = -32;
1518                 hull->clip_mins[2] = -24;
1519                 hull->clip_maxs[0] = 32;
1520                 hull->clip_maxs[1] = 32;
1521                 hull->clip_maxs[2] = 64;
1522         }
1523
1524         for (i=0 ; i<count ; i++, out++, in++)
1525         {
1526                 out->planenum = LittleLong(in->planenum);
1527                 out->children[0] = LittleShort(in->children[0]);
1528                 out->children[1] = LittleShort(in->children[1]);
1529                 if (out->children[0] >= count || out->children[1] >= count)
1530                         Host_Error("Corrupt clipping hull (out of range child)\n");
1531         }
1532 }
1533
1534 /*
1535 =================
1536 Mod_MakeHull0
1537
1538 Duplicate the drawing hull structure as a clipping hull
1539 =================
1540 */
1541 static void Mod_MakeHull0 (void)
1542 {
1543         mnode_t         *in;
1544         dclipnode_t *out;
1545         int                     i;
1546         hull_t          *hull;
1547
1548         hull = &loadmodel->hulls[0];
1549
1550         in = loadmodel->nodes;
1551         out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1552
1553         hull->clipnodes = out;
1554         hull->firstclipnode = 0;
1555         hull->lastclipnode = loadmodel->numnodes - 1;
1556         hull->planes = loadmodel->planes;
1557
1558         for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1559         {
1560                 out->planenum = in->plane - loadmodel->planes;
1561                 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1562                 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1563         }
1564 }
1565
1566 /*
1567 =================
1568 Mod_LoadMarksurfaces
1569 =================
1570 */
1571 static void Mod_LoadMarksurfaces (lump_t *l)
1572 {
1573         int             i, j;
1574         short   *in;
1575
1576         in = (void *)(mod_base + l->fileofs);
1577         if (l->filelen % sizeof(*in))
1578                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1579         loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1580         loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(msurface_t *));
1581
1582         for (i = 0;i < loadmodel->nummarksurfaces;i++)
1583         {
1584                 j = (unsigned) LittleShort(in[i]);
1585                 if (j >= loadmodel->numsurfaces)
1586                         Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1587                 loadmodel->marksurfaces[i] = loadmodel->surfaces + j;
1588         }
1589 }
1590
1591 /*
1592 =================
1593 Mod_LoadSurfedges
1594 =================
1595 */
1596 static void Mod_LoadSurfedges (lump_t *l)
1597 {
1598         int             i;
1599         int             *in;
1600
1601         in = (void *)(mod_base + l->fileofs);
1602         if (l->filelen % sizeof(*in))
1603                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1604         loadmodel->numsurfedges = l->filelen / sizeof(*in);
1605         loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1606
1607         for (i = 0;i < loadmodel->numsurfedges;i++)
1608                 loadmodel->surfedges[i] = LittleLong (in[i]);
1609 }
1610
1611
1612 /*
1613 =================
1614 Mod_LoadPlanes
1615 =================
1616 */
1617 static void Mod_LoadPlanes (lump_t *l)
1618 {
1619         int                     i;
1620         mplane_t        *out;
1621         dplane_t        *in;
1622
1623         in = (void *)(mod_base + l->fileofs);
1624         if (l->filelen % sizeof(*in))
1625                 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
1626
1627         loadmodel->numplanes = l->filelen / sizeof(*in);
1628         loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
1629
1630         for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
1631         {
1632                 out->normal[0] = LittleFloat (in->normal[0]);
1633                 out->normal[1] = LittleFloat (in->normal[1]);
1634                 out->normal[2] = LittleFloat (in->normal[2]);
1635                 out->dist = LittleFloat (in->dist);
1636
1637                 // LordHavoc: recalculated by PlaneClassify, FIXME: validate type and report error if type does not match normal?
1638 //              out->type = LittleLong (in->type);
1639                 PlaneClassify(out);
1640         }
1641 }
1642
1643 #define MAX_POINTS_ON_WINDING 64
1644
1645 typedef struct
1646 {
1647         int numpoints;
1648         int padding;
1649         double points[8][3]; // variable sized
1650 }
1651 winding_t;
1652
1653 /*
1654 ==================
1655 NewWinding
1656 ==================
1657 */
1658 static winding_t *NewWinding (int points)
1659 {
1660         winding_t *w;
1661         int size;
1662
1663         if (points > MAX_POINTS_ON_WINDING)
1664                 Sys_Error("NewWinding: too many points\n");
1665
1666         size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1667         w = Mem_Alloc(loadmodel->mempool, size);
1668         memset (w, 0, size);
1669
1670         return w;
1671 }
1672
1673 static void FreeWinding (winding_t *w)
1674 {
1675         Mem_Free(w);
1676 }
1677
1678 /*
1679 =================
1680 BaseWindingForPlane
1681 =================
1682 */
1683 static winding_t *BaseWindingForPlane (mplane_t *p)
1684 {
1685         double org[3], vright[3], vup[3], normal[3];
1686         winding_t *w;
1687
1688         VectorCopy(p->normal, normal);
1689         VectorVectorsDouble(normal, vright, vup);
1690
1691         VectorScale (vup, 1024.0*1024.0*1024.0, vup);
1692         VectorScale (vright, 1024.0*1024.0*1024.0, vright);
1693
1694         // project a really big axis aligned box onto the plane
1695         w = NewWinding (4);
1696
1697         VectorScale (p->normal, p->dist, org);
1698
1699         VectorSubtract (org, vright, w->points[0]);
1700         VectorAdd (w->points[0], vup, w->points[0]);
1701
1702         VectorAdd (org, vright, w->points[1]);
1703         VectorAdd (w->points[1], vup, w->points[1]);
1704
1705         VectorAdd (org, vright, w->points[2]);
1706         VectorSubtract (w->points[2], vup, w->points[2]);
1707
1708         VectorSubtract (org, vright, w->points[3]);
1709         VectorSubtract (w->points[3], vup, w->points[3]);
1710
1711         w->numpoints = 4;
1712
1713         return w;
1714 }
1715
1716 /*
1717 ==================
1718 ClipWinding
1719
1720 Clips the winding to the plane, returning the new winding on the positive side
1721 Frees the input winding.
1722 If keepon is true, an exactly on-plane winding will be saved, otherwise
1723 it will be clipped away.
1724 ==================
1725 */
1726 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1727 {
1728         double  dists[MAX_POINTS_ON_WINDING + 1];
1729         int             sides[MAX_POINTS_ON_WINDING + 1];
1730         int             counts[3];
1731         double  dot;
1732         int             i, j;
1733         double  *p1, *p2;
1734         double  mid[3];
1735         winding_t       *neww;
1736         int             maxpts;
1737
1738         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1739
1740         // determine sides for each point
1741         for (i = 0;i < in->numpoints;i++)
1742         {
1743                 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1744                 if (dot > ON_EPSILON)
1745                         sides[i] = SIDE_FRONT;
1746                 else if (dot < -ON_EPSILON)
1747                         sides[i] = SIDE_BACK;
1748                 else
1749                         sides[i] = SIDE_ON;
1750                 counts[sides[i]]++;
1751         }
1752         sides[i] = sides[0];
1753         dists[i] = dists[0];
1754
1755         if (keepon && !counts[0] && !counts[1])
1756                 return in;
1757
1758         if (!counts[0])
1759         {
1760                 FreeWinding (in);
1761                 return NULL;
1762         }
1763         if (!counts[1])
1764                 return in;
1765
1766         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
1767         if (maxpts > MAX_POINTS_ON_WINDING)
1768                 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1769
1770         neww = NewWinding (maxpts);
1771
1772         for (i = 0;i < in->numpoints;i++)
1773         {
1774                 if (neww->numpoints >= maxpts)
1775                         Sys_Error ("ClipWinding: points exceeded estimate");
1776
1777                 p1 = in->points[i];
1778
1779                 if (sides[i] == SIDE_ON)
1780                 {
1781                         VectorCopy (p1, neww->points[neww->numpoints]);
1782                         neww->numpoints++;
1783                         continue;
1784                 }
1785
1786                 if (sides[i] == SIDE_FRONT)
1787                 {
1788                         VectorCopy (p1, neww->points[neww->numpoints]);
1789                         neww->numpoints++;
1790                 }
1791
1792                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1793                         continue;
1794
1795                 // generate a split point
1796                 p2 = in->points[(i+1)%in->numpoints];
1797
1798                 dot = dists[i] / (dists[i]-dists[i+1]);
1799                 for (j = 0;j < 3;j++)
1800                 {       // avoid round off error when possible
1801                         if (split->normal[j] == 1)
1802                                 mid[j] = split->dist;
1803                         else if (split->normal[j] == -1)
1804                                 mid[j] = -split->dist;
1805                         else
1806                                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1807                 }
1808
1809                 VectorCopy (mid, neww->points[neww->numpoints]);
1810                 neww->numpoints++;
1811         }
1812
1813         // free the original winding
1814         FreeWinding (in);
1815
1816         // debugging
1817         //Mem_CheckSentinels(neww);
1818
1819         return neww;
1820 }
1821
1822
1823 /*
1824 ==================
1825 DivideWinding
1826
1827 Divides a winding by a plane, producing one or two windings.  The
1828 original winding is not damaged or freed.  If only on one side, the
1829 returned winding will be the input winding.  If on both sides, two
1830 new windings will be created.
1831 ==================
1832 */
1833 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1834 {
1835         double  dists[MAX_POINTS_ON_WINDING + 1];
1836         int             sides[MAX_POINTS_ON_WINDING + 1];
1837         int             counts[3];
1838         double  dot;
1839         int             i, j;
1840         double  *p1, *p2;
1841         double  mid[3];
1842         winding_t       *f, *b;
1843         int             maxpts;
1844
1845         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1846
1847         // determine sides for each point
1848         for (i = 0;i < in->numpoints;i++)
1849         {
1850                 dot = DotProduct (in->points[i], split->normal);
1851                 dot -= split->dist;
1852                 dists[i] = dot;
1853                 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1854                 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1855                 else sides[i] = SIDE_ON;
1856                 counts[sides[i]]++;
1857         }
1858         sides[i] = sides[0];
1859         dists[i] = dists[0];
1860
1861         *front = *back = NULL;
1862
1863         if (!counts[0])
1864         {
1865                 *back = in;
1866                 return;
1867         }
1868         if (!counts[1])
1869         {
1870                 *front = in;
1871                 return;
1872         }
1873
1874         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
1875
1876         if (maxpts > MAX_POINTS_ON_WINDING)
1877                 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1878
1879         *front = f = NewWinding (maxpts);
1880         *back = b = NewWinding (maxpts);
1881
1882         for (i = 0;i < in->numpoints;i++)
1883         {
1884                 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
1885                         Sys_Error ("DivideWinding: points exceeded estimate");
1886
1887                 p1 = in->points[i];
1888
1889                 if (sides[i] == SIDE_ON)
1890                 {
1891                         VectorCopy (p1, f->points[f->numpoints]);
1892                         f->numpoints++;
1893                         VectorCopy (p1, b->points[b->numpoints]);
1894                         b->numpoints++;
1895                         continue;
1896                 }
1897
1898                 if (sides[i] == SIDE_FRONT)
1899                 {
1900                         VectorCopy (p1, f->points[f->numpoints]);
1901                         f->numpoints++;
1902                 }
1903                 else if (sides[i] == SIDE_BACK)
1904                 {
1905                         VectorCopy (p1, b->points[b->numpoints]);
1906                         b->numpoints++;
1907                 }
1908
1909                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1910                         continue;
1911
1912                 // generate a split point
1913                 p2 = in->points[(i+1)%in->numpoints];
1914
1915                 dot = dists[i] / (dists[i]-dists[i+1]);
1916                 for (j = 0;j < 3;j++)
1917                 {       // avoid round off error when possible
1918                         if (split->normal[j] == 1)
1919                                 mid[j] = split->dist;
1920                         else if (split->normal[j] == -1)
1921                                 mid[j] = -split->dist;
1922                         else
1923                                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1924                 }
1925
1926                 VectorCopy (mid, f->points[f->numpoints]);
1927                 f->numpoints++;
1928                 VectorCopy (mid, b->points[b->numpoints]);
1929                 b->numpoints++;
1930         }
1931
1932         // debugging
1933         //Mem_CheckSentinels(f);
1934         //Mem_CheckSentinels(b);
1935 }
1936
1937 typedef struct portal_s
1938 {
1939         mplane_t plane;
1940         mnode_t *nodes[2];              // [0] = front side of plane
1941         struct portal_s *next[2];
1942         winding_t *winding;
1943         struct portal_s *chain; // all portals are linked into a list
1944 }
1945 portal_t;
1946
1947 static portal_t *portalchain;
1948
1949 /*
1950 ===========
1951 AllocPortal
1952 ===========
1953 */
1954 static portal_t *AllocPortal (void)
1955 {
1956         portal_t *p;
1957         p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
1958         //memset(p, 0, sizeof(portal_t));
1959         p->chain = portalchain;
1960         portalchain = p;
1961         return p;
1962 }
1963
1964 static void FreePortal(portal_t *p)
1965 {
1966         Mem_Free(p);
1967 }
1968
1969 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
1970 {
1971         // calculate children first
1972         if (node->children[0]->contents >= 0)
1973                 Mod_RecursiveRecalcNodeBBox(node->children[0]);
1974         if (node->children[1]->contents >= 0)
1975                 Mod_RecursiveRecalcNodeBBox(node->children[1]);
1976
1977         // make combined bounding box from children
1978         node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
1979         node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
1980         node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
1981         node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
1982         node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
1983         node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
1984 }
1985
1986 static void Mod_FinalizePortals(void)
1987 {
1988         int i, j, numportals, numpoints;
1989         portal_t *p, *pnext;
1990         mportal_t *portal;
1991         mvertex_t *point;
1992         mleaf_t *leaf, *endleaf;
1993         winding_t *w;
1994
1995         //Mem_CheckSentinelsGlobal();
1996
1997         // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
1998         leaf = loadmodel->leafs;
1999         endleaf = leaf + loadmodel->numleafs;
2000         for (;leaf < endleaf;leaf++)
2001         {
2002                 VectorSet(leaf->mins,  2000000000,  2000000000,  2000000000);
2003                 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2004         }
2005         p = portalchain;
2006         while(p)
2007         {
2008                 if (p->winding)
2009                 {
2010                         for (i = 0;i < 2;i++)
2011                         {
2012                                 leaf = (mleaf_t *)p->nodes[i];
2013                                 w = p->winding;
2014                                 for (j = 0;j < w->numpoints;j++)
2015                                 {
2016                                         if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2017                                         if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2018                                         if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2019                                         if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2020                                         if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2021                                         if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2022                                 }
2023                         }
2024                 }
2025                 p = p->chain;
2026         }
2027
2028         Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2029
2030         //Mem_CheckSentinelsGlobal();
2031
2032         // tally up portal and point counts
2033         p = portalchain;
2034         numportals = 0;
2035         numpoints = 0;
2036         while(p)
2037         {
2038                 // note: this check must match the one below or it will usually corrupt memory
2039                 // 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
2040                 if (p->winding && p->nodes[0] != p->nodes[1]
2041                  && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2042                  && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2043                 {
2044                         numportals += 2;
2045                         numpoints += p->winding->numpoints * 2;
2046                 }
2047                 p = p->chain;
2048         }
2049         loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2050         loadmodel->numportals = numportals;
2051         loadmodel->portalpoints = (void *) ((long) loadmodel->portals + numportals * sizeof(mportal_t));
2052         loadmodel->numportalpoints = numpoints;
2053         // clear all leaf portal chains
2054         for (i = 0;i < loadmodel->numleafs;i++)
2055                 loadmodel->leafs[i].portals = NULL;
2056         // process all portals in the global portal chain, while freeing them
2057         portal = loadmodel->portals;
2058         point = loadmodel->portalpoints;
2059         p = portalchain;
2060         portalchain = NULL;
2061         while (p)
2062         {
2063                 pnext = p->chain;
2064
2065                 if (p->winding)
2066                 {
2067                         // note: this check must match the one above or it will usually corrupt memory
2068                         // 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
2069                         if (p->nodes[0] != p->nodes[1]
2070                          && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2071                          && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2072                         {
2073                                 // first make the back to front portal (forward portal)
2074                                 portal->points = point;
2075                                 portal->numpoints = p->winding->numpoints;
2076                                 portal->plane.dist = p->plane.dist;
2077                                 VectorCopy(p->plane.normal, portal->plane.normal);
2078                                 portal->here = (mleaf_t *)p->nodes[1];
2079                                 portal->past = (mleaf_t *)p->nodes[0];
2080                                 // copy points
2081                                 for (j = 0;j < portal->numpoints;j++)
2082                                 {
2083                                         VectorCopy(p->winding->points[j], point->position);
2084                                         point++;
2085                                 }
2086                                 PlaneClassify(&portal->plane);
2087
2088                                 // link into leaf's portal chain
2089                                 portal->next = portal->here->portals;
2090                                 portal->here->portals = portal;
2091
2092                                 // advance to next portal
2093                                 portal++;
2094
2095                                 // then make the front to back portal (backward portal)
2096                                 portal->points = point;
2097                                 portal->numpoints = p->winding->numpoints;
2098                                 portal->plane.dist = -p->plane.dist;
2099                                 VectorNegate(p->plane.normal, portal->plane.normal);
2100                                 portal->here = (mleaf_t *)p->nodes[0];
2101                                 portal->past = (mleaf_t *)p->nodes[1];
2102                                 // copy points
2103                                 for (j = portal->numpoints - 1;j >= 0;j--)
2104                                 {
2105                                         VectorCopy(p->winding->points[j], point->position);
2106                                         point++;
2107                                 }
2108                                 PlaneClassify(&portal->plane);
2109
2110                                 // link into leaf's portal chain
2111                                 portal->next = portal->here->portals;
2112                                 portal->here->portals = portal;
2113
2114                                 // advance to next portal
2115                                 portal++;
2116                         }
2117                         FreeWinding(p->winding);
2118                 }
2119                 FreePortal(p);
2120                 p = pnext;
2121         }
2122
2123         //Mem_CheckSentinelsGlobal();
2124 }
2125
2126 /*
2127 =============
2128 AddPortalToNodes
2129 =============
2130 */
2131 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2132 {
2133         if (!front)
2134                 Host_Error ("AddPortalToNodes: NULL front node");
2135         if (!back)
2136                 Host_Error ("AddPortalToNodes: NULL back node");
2137         if (p->nodes[0] || p->nodes[1])
2138                 Host_Error ("AddPortalToNodes: already included");
2139         // 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
2140
2141         p->nodes[0] = front;
2142         p->next[0] = (portal_t *)front->portals;
2143         front->portals = (mportal_t *)p;
2144
2145         p->nodes[1] = back;
2146         p->next[1] = (portal_t *)back->portals;
2147         back->portals = (mportal_t *)p;
2148 }
2149
2150 /*
2151 =============
2152 RemovePortalFromNode
2153 =============
2154 */
2155 static void RemovePortalFromNodes(portal_t *portal)
2156 {
2157         int i;
2158         mnode_t *node;
2159         void **portalpointer;
2160         portal_t *t;
2161         for (i = 0;i < 2;i++)
2162         {
2163                 node = portal->nodes[i];
2164
2165                 portalpointer = (void **) &node->portals;
2166                 while (1)
2167                 {
2168                         t = *portalpointer;
2169                         if (!t)
2170                                 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2171
2172                         if (t == portal)
2173                         {
2174                                 if (portal->nodes[0] == node)
2175                                 {
2176                                         *portalpointer = portal->next[0];
2177                                         portal->nodes[0] = NULL;
2178                                 }
2179                                 else if (portal->nodes[1] == node)
2180                                 {
2181                                         *portalpointer = portal->next[1];
2182                                         portal->nodes[1] = NULL;
2183                                 }
2184                                 else
2185                                         Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2186                                 break;
2187                         }
2188
2189                         if (t->nodes[0] == node)
2190                                 portalpointer = (void **) &t->next[0];
2191                         else if (t->nodes[1] == node)
2192                                 portalpointer = (void **) &t->next[1];
2193                         else
2194                                 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2195                 }
2196         }
2197 }
2198
2199 static void Mod_RecursiveNodePortals (mnode_t *node)
2200 {
2201         int side;
2202         mnode_t *front, *back, *other_node;
2203         mplane_t clipplane, *plane;
2204         portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2205         winding_t *nodeportalwinding, *frontwinding, *backwinding;
2206
2207         //      CheckLeafPortalConsistancy (node);
2208
2209         // if a leaf, we're done
2210         if (node->contents)
2211                 return;
2212
2213         plane = node->plane;
2214
2215         front = node->children[0];
2216         back = node->children[1];
2217         if (front == back)
2218                 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2219
2220         // create the new portal by generating a polygon for the node plane,
2221         // and clipping it by all of the other portals (which came from nodes above this one)
2222         nodeportal = AllocPortal ();
2223         nodeportal->plane = *node->plane;
2224
2225         nodeportalwinding = BaseWindingForPlane (node->plane);
2226         //Mem_CheckSentinels(nodeportalwinding);
2227         side = 0;       // shut up compiler warning
2228         for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2229         {
2230                 clipplane = portal->plane;
2231                 if (portal->nodes[0] == portal->nodes[1])
2232                         Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2233                 if (portal->nodes[0] == node)
2234                         side = 0;
2235                 else if (portal->nodes[1] == node)
2236                 {
2237                         clipplane.dist = -clipplane.dist;
2238                         VectorNegate (clipplane.normal, clipplane.normal);
2239                         side = 1;
2240                 }
2241                 else
2242                         Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2243
2244                 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2245                 if (!nodeportalwinding)
2246                 {
2247                         printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2248                         break;
2249                 }
2250         }
2251
2252         if (nodeportalwinding)
2253         {
2254                 // if the plane was not clipped on all sides, there was an error
2255                 nodeportal->winding = nodeportalwinding;
2256                 AddPortalToNodes (nodeportal, front, back);
2257         }
2258
2259         // split the portals of this node along this node's plane and assign them to the children of this node
2260         // (migrating the portals downward through the tree)
2261         for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2262         {
2263                 if (portal->nodes[0] == portal->nodes[1])
2264                         Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2265                 if (portal->nodes[0] == node)
2266                         side = 0;
2267                 else if (portal->nodes[1] == node)
2268                         side = 1;
2269                 else
2270                         Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2271                 nextportal = portal->next[side];
2272
2273                 other_node = portal->nodes[!side];
2274                 RemovePortalFromNodes (portal);
2275
2276                 // cut the portal into two portals, one on each side of the node plane
2277                 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2278
2279                 if (!frontwinding)
2280                 {
2281                         if (side == 0)
2282                                 AddPortalToNodes (portal, back, other_node);
2283                         else
2284                                 AddPortalToNodes (portal, other_node, back);
2285                         continue;
2286                 }
2287                 if (!backwinding)
2288                 {
2289                         if (side == 0)
2290                                 AddPortalToNodes (portal, front, other_node);
2291                         else
2292                                 AddPortalToNodes (portal, other_node, front);
2293                         continue;
2294                 }
2295
2296                 // the winding is split
2297                 splitportal = AllocPortal ();
2298                 temp = splitportal->chain;
2299                 *splitportal = *portal;
2300                 splitportal->chain = temp;
2301                 splitportal->winding = backwinding;
2302                 FreeWinding (portal->winding);
2303                 portal->winding = frontwinding;
2304
2305                 if (side == 0)
2306                 {
2307                         AddPortalToNodes (portal, front, other_node);
2308                         AddPortalToNodes (splitportal, back, other_node);
2309                 }
2310                 else
2311                 {
2312                         AddPortalToNodes (portal, other_node, front);
2313                         AddPortalToNodes (splitportal, other_node, back);
2314                 }
2315         }
2316
2317         Mod_RecursiveNodePortals(front);
2318         Mod_RecursiveNodePortals(back);
2319 }
2320
2321 /*
2322 void Mod_MakeOutsidePortals(mnode_t *node)
2323 {
2324         int                     i, j;
2325         portal_t        *p, *portals[6];
2326         mnode_t         *outside_node;
2327
2328         outside_node = Mem_Alloc(loadmodel->mempool, sizeof(mnode_t));
2329         outside_node->contents = CONTENTS_SOLID;
2330         outside_node->portals = NULL;
2331
2332         for (i = 0;i < 3;i++)
2333         {
2334                 for (j = 0;j < 2;j++)
2335                 {
2336                         portals[j*3 + i] = p = AllocPortal ();
2337                         memset (&p->plane, 0, sizeof(mplane_t));
2338                         p->plane.normal[i] = j ? -1 : 1;
2339                         p->plane.dist = -65536;
2340                         p->winding = BaseWindingForPlane (&p->plane);
2341                         if (j)
2342                                 AddPortalToNodes (p, outside_node, node);
2343                         else
2344                                 AddPortalToNodes (p, node, outside_node);
2345                 }
2346         }
2347
2348         // clip the basewindings by all the other planes
2349         for (i = 0;i < 6;i++)
2350         {
2351                 for (j = 0;j < 6;j++)
2352                 {
2353                         if (j == i)
2354                                 continue;
2355                         portals[i]->winding = ClipWinding (portals[i]->winding, &portals[j]->plane, true);
2356                 }
2357         }
2358 }
2359 */
2360
2361 static void Mod_MakePortals(void)
2362 {
2363 //      Con_Printf("building portals for %s\n", loadmodel->name);
2364
2365         portalchain = NULL;
2366 //      Mod_MakeOutsidePortals (loadmodel->nodes);
2367         Mod_RecursiveNodePortals (loadmodel->nodes);
2368         Mod_FinalizePortals();
2369 }
2370
2371 /*
2372 =================
2373 Mod_LoadBrushModel
2374 =================
2375 */
2376 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2377 {
2378         int                     i, j;
2379         dheader_t       *header;
2380         dmodel_t        *bm;
2381         mempool_t       *mainmempool;
2382         char            *loadname;
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 = (qbyte *)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         loadname = mod->name;
2449
2450 //
2451 // set up the submodels (FIXME: this is confusing)
2452 //
2453         for (i = 0;i < mod->numsubmodels;i++)
2454         {
2455                 int k, l;
2456                 float dist, modelyawradius, modelradius, *vec;
2457                 msurface_t *surf;
2458
2459                 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2460                 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2461                 modelyawradius = 0;
2462                 modelradius = 0;
2463
2464                 bm = &mod->submodels[i];
2465
2466                 mod->hulls[0].firstclipnode = bm->headnode[0];
2467                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2468                 {
2469                         mod->hulls[j].firstclipnode = bm->headnode[j];
2470                         mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2471                 }
2472
2473                 mod->firstmodelsurface = bm->firstface;
2474                 mod->nummodelsurfaces = bm->numfaces;
2475
2476                 mod->DrawSky = NULL;
2477                 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2478                 for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2479                 {
2480                         // we only need to have a drawsky function if it is used (usually only on world model)
2481                         if (surf->shader == &Cshader_sky)
2482                                 mod->DrawSky = R_DrawBrushModelSky;
2483                         for (k = 0;k < surf->numedges;k++)
2484                         {
2485                                 l = mod->surfedges[k + surf->firstedge];
2486                                 if (l > 0)
2487                                         vec = mod->vertexes[mod->edges[l].v[0]].position;
2488                                 else
2489                                         vec = mod->vertexes[mod->edges[-l].v[1]].position;
2490                                 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2491                                 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2492                                 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2493                                 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2494                                 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2495                                 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2496                                 dist = vec[0]*vec[0]+vec[1]*vec[1];
2497                                 if (modelyawradius < dist)
2498                                         modelyawradius = dist;
2499                                 dist += vec[2]*vec[2];
2500                                 if (modelradius < dist)
2501                                         modelradius = dist;
2502                         }
2503                 }
2504                 modelyawradius = sqrt(modelyawradius);
2505                 modelradius = sqrt(modelradius);
2506                 mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2507                 mod->yawmins[2] = mod->normalmins[2];
2508                 mod->yawmaxs[2] = mod->normalmaxs[2];
2509                 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2510                 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2511 //              mod->modelradius = modelradius;
2512                 // LordHavoc: check for empty submodels (lacrima.bsp has such a glitch)
2513                 if (mod->normalmins[0] > mod->normalmaxs[0] || mod->normalmins[1] > mod->normalmaxs[1] || mod->normalmins[2] > mod->normalmaxs[2])
2514                 {
2515                         Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2516                         VectorClear(mod->normalmins);
2517                         VectorClear(mod->normalmaxs);
2518                         VectorClear(mod->yawmins);
2519                         VectorClear(mod->yawmaxs);
2520                         VectorClear(mod->rotatedmins);
2521                         VectorClear(mod->rotatedmaxs);
2522                         //mod->modelradius = 0;
2523                 }
2524
2525 //              VectorCopy (bm->maxs, mod->maxs);
2526 //              VectorCopy (bm->mins, mod->mins);
2527
2528 //              mod->radius = RadiusFromBounds (mod->mins, mod->maxs);
2529
2530                 mod->numleafs = bm->visleafs;
2531
2532                 mod->SERAddEntity = Mod_Brush_SERAddEntity;
2533                 mod->Draw = R_DrawBrushModelNormal;
2534                 mod->DrawShadow = NULL;
2535
2536                 Mod_BrushSortedSurfaces(mod, mainmempool);
2537
2538                 // LordHavoc: only register submodels if it is the world
2539                 // (prevents bsp models from replacing world submodels)
2540                 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2541                 {
2542                         char    name[10];
2543                         // duplicate the basic information
2544                         sprintf (name, "*%i", i+1);
2545                         loadmodel = Mod_FindName (name);
2546                         *loadmodel = *mod;
2547                         strcpy (loadmodel->name, name);
2548                         // textures and memory belong to the main model
2549                         loadmodel->texturepool = NULL;
2550                         loadmodel->mempool = NULL;
2551                         mod = loadmodel;
2552                 }
2553         }
2554 //      Mem_CheckSentinelsGlobal();
2555 }