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