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