]> icculus.org git repositories - divverent/darkplaces.git/blob - model_brush.c
GL_SetupView_* functions added, they replace most of R_Mesh_Start's functionality...
[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 14 parameters (origin[0] origin[1] origin[2] falloff light[0] light[1] light[2] subtract spotdir[0] spotdir[1] spotdir[2] spotcone distancebias style)\n", a, n + 1);
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(const char *data)
682 {
683         char key[128], value[4096];
684         char wadname[128];
685         int i, j, k;
686         if (!data)
687                 return;
688         if (!COM_ParseToken(&data))
689                 return; // error
690         if (com_token[0] != '{')
691                 return; // error
692         while (1)
693         {
694                 if (!COM_ParseToken(&data))
695                         return; // error
696                 if (com_token[0] == '}')
697                         break; // end of worldspawn
698                 if (com_token[0] == '_')
699                         strcpy(key, com_token + 1);
700                 else
701                         strcpy(key, com_token);
702                 while (key[strlen(key)-1] == ' ') // remove trailing spaces
703                         key[strlen(key)-1] = 0;
704                 if (!COM_ParseToken(&data))
705                         return; // error
706                 strcpy(value, com_token);
707                 if (!strcmp("wad", key)) // for HalfLife maps
708                 {
709                         if (loadmodel->ishlbsp)
710                         {
711                                 j = 0;
712                                 for (i = 0;i < 4096;i++)
713                                         if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
714                                                 break;
715                                 if (value[i])
716                                 {
717                                         for (;i < 4096;i++)
718                                         {
719                                                 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
720                                                 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
721                                                         j = i+1;
722                                                 else if (value[i] == ';' || value[i] == 0)
723                                                 {
724                                                         k = value[i];
725                                                         value[i] = 0;
726                                                         strcpy(wadname, "textures/");
727                                                         strcat(wadname, &value[j]);
728                                                         W_LoadTextureWadFile (wadname, false);
729                                                         j = i+1;
730                                                         if (!k)
731                                                                 break;
732                                                 }
733                                         }
734                                 }
735                         }
736                 }
737         }
738 }
739
740 /*
741 =================
742 Mod_LoadEntities
743 =================
744 */
745 static void Mod_LoadEntities (lump_t *l)
746 {
747         loadmodel->entities = NULL;
748         if (!l->filelen)
749                 return;
750         loadmodel->entities = Mem_Alloc(loadmodel->mempool, l->filelen);
751         memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
752         if (loadmodel->ishlbsp)
753                 Mod_ParseWadsFromEntityLump(loadmodel->entities);
754 }
755
756
757 /*
758 =================
759 Mod_LoadVertexes
760 =================
761 */
762 static void Mod_LoadVertexes (lump_t *l)
763 {
764         dvertex_t       *in;
765         mvertex_t       *out;
766         int                     i, count;
767
768         in = (void *)(mod_base + l->fileofs);
769         if (l->filelen % sizeof(*in))
770                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
771         count = l->filelen / sizeof(*in);
772         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
773
774         loadmodel->vertexes = out;
775         loadmodel->numvertexes = count;
776
777         for ( i=0 ; i<count ; i++, in++, out++)
778         {
779                 out->position[0] = LittleFloat (in->point[0]);
780                 out->position[1] = LittleFloat (in->point[1]);
781                 out->position[2] = LittleFloat (in->point[2]);
782         }
783 }
784
785 /*
786 =================
787 Mod_LoadSubmodels
788 =================
789 */
790 static void Mod_LoadSubmodels (lump_t *l)
791 {
792         dmodel_t        *in;
793         dmodel_t        *out;
794         int                     i, j, count;
795
796         in = (void *)(mod_base + l->fileofs);
797         if (l->filelen % sizeof(*in))
798                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
799         count = l->filelen / sizeof(*in);
800         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
801
802         loadmodel->submodels = out;
803         loadmodel->numsubmodels = count;
804
805         for ( i=0 ; i<count ; i++, in++, out++)
806         {
807                 for (j=0 ; j<3 ; j++)
808                 {
809                         // spread the mins / maxs by a pixel
810                         out->mins[j] = LittleFloat (in->mins[j]) - 1;
811                         out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
812                         out->origin[j] = LittleFloat (in->origin[j]);
813                 }
814                 for (j=0 ; j<MAX_MAP_HULLS ; j++)
815                         out->headnode[j] = LittleLong (in->headnode[j]);
816                 out->visleafs = LittleLong (in->visleafs);
817                 out->firstface = LittleLong (in->firstface);
818                 out->numfaces = LittleLong (in->numfaces);
819         }
820 }
821
822 /*
823 =================
824 Mod_LoadEdges
825 =================
826 */
827 static void Mod_LoadEdges (lump_t *l)
828 {
829         dedge_t *in;
830         medge_t *out;
831         int     i, count;
832
833         in = (void *)(mod_base + l->fileofs);
834         if (l->filelen % sizeof(*in))
835                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
836         count = l->filelen / sizeof(*in);
837         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
838
839         loadmodel->edges = out;
840         loadmodel->numedges = count;
841
842         for ( i=0 ; i<count ; i++, in++, out++)
843         {
844                 out->v[0] = (unsigned short)LittleShort(in->v[0]);
845                 out->v[1] = (unsigned short)LittleShort(in->v[1]);
846         }
847 }
848
849 /*
850 =================
851 Mod_LoadTexinfo
852 =================
853 */
854 static void Mod_LoadTexinfo (lump_t *l)
855 {
856         texinfo_t *in;
857         mtexinfo_t *out;
858         int i, j, k, count, miptex;
859
860         in = (void *)(mod_base + l->fileofs);
861         if (l->filelen % sizeof(*in))
862                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
863         count = l->filelen / sizeof(*in);
864         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
865
866         loadmodel->texinfo = out;
867         loadmodel->numtexinfo = count;
868
869         for (i = 0;i < count;i++, in++, out++)
870         {
871                 for (k = 0;k < 2;k++)
872                         for (j = 0;j < 4;j++)
873                                 out->vecs[k][j] = LittleFloat (in->vecs[k][j]);
874
875                 miptex = LittleLong (in->miptex);
876                 out->flags = LittleLong (in->flags);
877
878                 out->texture = NULL;
879                 if (loadmodel->textures)
880                 {
881                         if ((unsigned int) miptex >= (unsigned int) loadmodel->numtextures)
882                                 Con_Printf ("error in model \"%s\": invalid miptex index %i (of %i)\n", loadmodel->name, miptex, loadmodel->numtextures);
883                         else
884                                 out->texture = loadmodel->textures + miptex;
885                 }
886                 if (out->flags & TEX_SPECIAL)
887                 {
888                         // if texture chosen is NULL or the shader needs a lightmap,
889                         // force to notexture water shader
890                         if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
891                                 out->texture = loadmodel->textures + (loadmodel->numtextures - 1);
892                 }
893                 else
894                 {
895                         // if texture chosen is NULL, force to notexture
896                         if (out->texture == NULL)
897                                 out->texture = loadmodel->textures + (loadmodel->numtextures - 2);
898                 }
899         }
900 }
901
902 /*
903 ================
904 CalcSurfaceExtents
905
906 Fills in s->texturemins[] and s->extents[]
907 ================
908 */
909 static void CalcSurfaceExtents (msurface_t *s)
910 {
911         float   mins[2], maxs[2], val;
912         int             i,j, e;
913         mvertex_t       *v;
914         mtexinfo_t      *tex;
915         int             bmins[2], bmaxs[2];
916
917         mins[0] = mins[1] = 999999999;
918         maxs[0] = maxs[1] = -999999999;
919
920         tex = s->texinfo;
921
922         for (i=0 ; i<s->numedges ; i++)
923         {
924                 e = loadmodel->surfedges[s->firstedge+i];
925                 if (e >= 0)
926                         v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
927                 else
928                         v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
929
930                 for (j=0 ; j<2 ; j++)
931                 {
932                         val = v->position[0] * tex->vecs[j][0] +
933                                 v->position[1] * tex->vecs[j][1] +
934                                 v->position[2] * tex->vecs[j][2] +
935                                 tex->vecs[j][3];
936                         if (val < mins[j])
937                                 mins[j] = val;
938                         if (val > maxs[j])
939                                 maxs[j] = val;
940                 }
941         }
942
943         for (i=0 ; i<2 ; i++)
944         {
945                 bmins[i] = floor(mins[i]/16);
946                 bmaxs[i] = ceil(maxs[i]/16);
947
948                 s->texturemins[i] = bmins[i] * 16;
949                 s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
950         }
951 }
952
953
954 void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs)
955 {
956         int             i, j;
957         float   *v;
958
959         mins[0] = mins[1] = mins[2] = 9999;
960         maxs[0] = maxs[1] = maxs[2] = -9999;
961         v = verts;
962         for (i = 0;i < numverts;i++)
963         {
964                 for (j = 0;j < 3;j++, v++)
965                 {
966                         if (*v < mins[j])
967                                 mins[j] = *v;
968                         if (*v > maxs[j])
969                                 maxs[j] = *v;
970                 }
971         }
972 }
973
974 #if 0
975 #define MAX_SUBDIVPOLYTRIANGLES 4096
976 #define MAX_SUBDIVPOLYVERTS (MAX_SUBDIVPOLYTRIANGLES * 3)
977
978 static int subdivpolyverts, subdivpolytriangles;
979 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
980 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
981
982 static int subdivpolylookupvert(vec3_t v)
983 {
984         int i;
985         for (i = 0;i < subdivpolyverts;i++)
986                 if (subdivpolyvert[i][0] == v[0]
987                  && subdivpolyvert[i][1] == v[1]
988                  && subdivpolyvert[i][2] == v[2])
989                         return i;
990         if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
991                 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
992         VectorCopy(v, subdivpolyvert[subdivpolyverts]);
993         return subdivpolyverts++;
994 }
995
996 static void SubdividePolygon (int numverts, float *verts)
997 {
998         int             i, i1, i2, i3, f, b, c, p;
999         vec3_t  mins, maxs, front[256], back[256];
1000         float   m, *pv, *cv, dist[256], frac;
1001
1002         if (numverts > 250)
1003                 Host_Error ("SubdividePolygon: ran out of verts in buffer");
1004
1005         BoundPoly (numverts, verts, mins, maxs);
1006
1007         for (i = 0;i < 3;i++)
1008         {
1009                 m = (mins[i] + maxs[i]) * 0.5;
1010                 m = r_subdivide_size.value * floor (m/r_subdivide_size.value + 0.5);
1011                 if (maxs[i] - m < 8)
1012                         continue;
1013                 if (m - mins[i] < 8)
1014                         continue;
1015
1016                 // cut it
1017                 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1018                         dist[c] = cv[i] - m;
1019
1020                 f = b = 0;
1021                 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1022                 {
1023                         if (dist[p] >= 0)
1024                         {
1025                                 VectorCopy (pv, front[f]);
1026                                 f++;
1027                         }
1028                         if (dist[p] <= 0)
1029                         {
1030                                 VectorCopy (pv, back[b]);
1031                                 b++;
1032                         }
1033                         if (dist[p] == 0 || dist[c] == 0)
1034                                 continue;
1035                         if ( (dist[p] > 0) != (dist[c] > 0) )
1036                         {
1037                                 // clip point
1038                                 frac = dist[p] / (dist[p] - dist[c]);
1039                                 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1040                                 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1041                                 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1042                                 f++;
1043                                 b++;
1044                         }
1045                 }
1046
1047                 SubdividePolygon (f, front[0]);
1048                 SubdividePolygon (b, back[0]);
1049                 return;
1050         }
1051
1052         i1 = subdivpolylookupvert(verts);
1053         i2 = subdivpolylookupvert(verts + 3);
1054         for (i = 2;i < numverts;i++)
1055         {
1056                 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1057                 {
1058                         Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1059                         return;
1060                 }
1061
1062                 i3 = subdivpolylookupvert(verts + i * 3);
1063                 subdivpolyindex[subdivpolytriangles][0] = i1;
1064                 subdivpolyindex[subdivpolytriangles][1] = i2;
1065                 subdivpolyindex[subdivpolytriangles][2] = i3;
1066                 i2 = i3;
1067                 subdivpolytriangles++;
1068         }
1069 }
1070
1071 /*
1072 ================
1073 Mod_GenerateWarpMesh
1074
1075 Breaks a polygon up along axial 64 unit
1076 boundaries so that turbulent and sky warps
1077 can be done reasonably.
1078 ================
1079 */
1080 void Mod_GenerateWarpMesh (msurface_t *surf)
1081 {
1082         int i, j;
1083         surfvertex_t *v;
1084         surfmesh_t *mesh;
1085
1086         subdivpolytriangles = 0;
1087         subdivpolyverts = 0;
1088         SubdividePolygon (surf->poly_numverts, surf->poly_verts);
1089         if (subdivpolytriangles < 1)
1090                 Host_Error("Mod_GenerateWarpMesh: no triangles?\n");
1091
1092         surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1093         mesh->numverts = subdivpolyverts;
1094         mesh->numtriangles = subdivpolytriangles;
1095         mesh->vertex = (surfvertex_t *)(mesh + 1);
1096         mesh->index = (int *)(mesh->vertex + mesh->numverts);
1097         memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1098
1099         for (i = 0;i < mesh->numtriangles;i++)
1100                 for (j = 0;j < 3;j++)
1101                         mesh->index[i*3+j] = subdivpolyindex[i][j];
1102
1103         for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1104         {
1105                 VectorCopy(subdivpolyvert[i], v->v);
1106                 v->st[0] = DotProduct (v->v, surf->texinfo->vecs[0]);
1107                 v->st[1] = DotProduct (v->v, surf->texinfo->vecs[1]);
1108         }
1109 }
1110 #endif
1111
1112 void Mod_GenerateWallMesh (msurface_t *surf, int vertexonly)
1113 {
1114         int i, iu, iv, *index, smax, tmax;
1115         float *in, s, t, u, v, ubase, vbase, uscale, vscale;
1116         surfmesh_t *mesh;
1117
1118         smax = surf->extents[0] >> 4;
1119         tmax = surf->extents[1] >> 4;
1120
1121         if (vertexonly)
1122         {
1123                 surf->lightmaptexturestride = 0;
1124                 surf->lightmaptexture = NULL;
1125                 uscale = 0;
1126                 vscale = 0;
1127                 ubase = 0;
1128                 vbase = 0;
1129         }
1130         else
1131         {
1132                 surf->flags |= SURF_LIGHTMAP;
1133                 if (r_miplightmaps.integer)
1134                 {
1135                         surf->lightmaptexturestride = (surf->extents[0]>>4)+1;
1136                         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);
1137                 }
1138                 else
1139                 {
1140                         surf->lightmaptexturestride = R_CompatibleFragmentWidth((surf->extents[0]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1141                         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);
1142                 }
1143                 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1144                 uscale = (uscale - ubase) * 16.0 / ((surf->extents[0] & ~15) + 16);
1145                 vscale = (vscale - vbase) * 16.0 / ((surf->extents[1] & ~15) + 16);
1146         }
1147
1148         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));
1149         mesh->numverts = surf->poly_numverts;
1150         mesh->numtriangles = surf->poly_numverts - 2;
1151         mesh->verts = (float *)(mesh + 1);
1152         mesh->st = mesh->verts + mesh->numverts * 4;
1153         mesh->uv = mesh->st + mesh->numverts * 2;
1154         mesh->ab = mesh->uv + mesh->numverts * 2;
1155         mesh->lightmapoffsets = (int *)(mesh->ab + mesh->numverts * 2);
1156         mesh->index = mesh->lightmapoffsets + mesh->numverts;
1157
1158         index = mesh->index;
1159         for (i = 0;i < mesh->numtriangles;i++)
1160         {
1161                 *index++ = 0;
1162                 *index++ = i + 1;
1163                 *index++ = i + 2;
1164         }
1165
1166         for (i = 0, in = surf->poly_verts;i < mesh->numverts;i++, in += 3)
1167         {
1168                 s = DotProduct (in, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1169                 t = DotProduct (in, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1170                 u = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1171                 v = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1172                 // LordHavoc: calc lightmap data offset for vertex lighting to use
1173                 iu = (int) u;
1174                 iv = (int) v;
1175                 iu = bound(0, iu, smax);
1176                 iv = bound(0, iv, tmax);
1177                 u = u * uscale + ubase;
1178                 v = v * vscale + vbase;
1179
1180                 mesh->verts[i * 4 + 0] = in[0];
1181                 mesh->verts[i * 4 + 1] = in[1];
1182                 mesh->verts[i * 4 + 2] = in[2];
1183                 mesh->st[i * 2 + 0] = s / surf->texinfo->texture->width;
1184                 mesh->st[i * 2 + 1] = t / surf->texinfo->texture->height;
1185                 mesh->uv[i * 2 + 0] = u;
1186                 mesh->uv[i * 2 + 1] = v;
1187                 mesh->ab[i * 2 + 0] = s * (1.0f / 16.0f);
1188                 mesh->ab[i * 2 + 1] = t * (1.0f / 16.0f);
1189                 mesh->lightmapoffsets[i] = ((iv * (smax+1) + iu) * 3);
1190         }
1191 }
1192
1193 void Mod_GenerateVertexMesh (msurface_t *surf)
1194 {
1195         int i, *index;
1196         float *in, s, t;
1197         surfmesh_t *mesh;
1198
1199         surf->lightmaptexturestride = 0;
1200         surf->lightmaptexture = NULL;
1201
1202         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));
1203         mesh->numverts = surf->poly_numverts;
1204         mesh->numtriangles = surf->poly_numverts - 2;
1205         mesh->verts = (float *)(mesh + 1);
1206         mesh->st = mesh->verts + mesh->numverts * 4;
1207         mesh->ab = mesh->st + mesh->numverts * 2;
1208         mesh->index = (int *)(mesh->ab + mesh->numverts * 2);
1209
1210         index = mesh->index;
1211         for (i = 0;i < mesh->numtriangles;i++)
1212         {
1213                 *index++ = 0;
1214                 *index++ = i + 1;
1215                 *index++ = i + 2;
1216         }
1217
1218         for (i = 0, in = surf->poly_verts;i < mesh->numverts;i++, in += 3)
1219         {
1220                 s = (DotProduct (in, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]);
1221                 t = (DotProduct (in, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]);
1222                 mesh->verts[i * 4 + 0] = in[0];
1223                 mesh->verts[i * 4 + 1] = in[1];
1224                 mesh->verts[i * 4 + 2] = in[2];
1225                 mesh->st[i * 2 + 0] = s / surf->texinfo->texture->width;
1226                 mesh->st[i * 2 + 1] = t / surf->texinfo->texture->height;
1227                 mesh->ab[i * 2 + 0] = s * (1.0f / 16.0f);
1228                 mesh->ab[i * 2 + 1] = t * (1.0f / 16.0f);
1229         }
1230 }
1231
1232 void Mod_GenerateSurfacePolygon (msurface_t *surf)
1233 {
1234         int i, lindex;
1235         float *vec, *vert, mins[3], maxs[3];
1236
1237         // convert edges back to a normal polygon
1238         surf->poly_numverts = surf->numedges;
1239         vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges);
1240         for (i = 0;i < surf->numedges;i++)
1241         {
1242                 lindex = loadmodel->surfedges[surf->firstedge + i];
1243                 if (lindex > 0)
1244                         vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
1245                 else
1246                         vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
1247                 VectorCopy (vec, vert);
1248                 vert += 3;
1249         }
1250         vert = surf->poly_verts;
1251         VectorCopy(vert, mins);
1252         VectorCopy(vert, maxs);
1253         vert += 3;
1254         for (i = 1;i < surf->poly_numverts;i++)
1255         {
1256                 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1257                 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1258                 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1259                 vert += 3;
1260         }
1261         VectorCopy(mins, surf->poly_mins);
1262         VectorCopy(maxs, surf->poly_maxs);
1263         surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1264         surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1265         surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1266 }
1267
1268 /*
1269 =================
1270 Mod_LoadFaces
1271 =================
1272 */
1273 static void Mod_LoadFaces (lump_t *l)
1274 {
1275         dface_t *in;
1276         msurface_t      *out;
1277         int i, count, surfnum, planenum, ssize, tsize;
1278
1279         in = (void *)(mod_base + l->fileofs);
1280         if (l->filelen % sizeof(*in))
1281                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1282         count = l->filelen / sizeof(*in);
1283         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1284
1285         loadmodel->surfaces = out;
1286         loadmodel->numsurfaces = count;
1287         loadmodel->surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1288         loadmodel->surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1289
1290         for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
1291         {
1292                 out->number = surfnum;
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(int));
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] = 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