]> icculus.org git repositories - divverent/darkplaces.git/blob - model_brush.c
LoadTGA now loads colormapped and greyscale targas (as found in qe1m1 project)
[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                                                 temp[0] = bound(surf->poly_mins[0], e->origin[0], surf->poly_maxs[0]) - e->origin[0];
787                                                 temp[1] = bound(surf->poly_mins[1], e->origin[1], surf->poly_maxs[1]) - e->origin[1];
788                                                 temp[2] = bound(surf->poly_mins[2], e->origin[2], surf->poly_maxs[2]) - e->origin[2];
789                                                 if (DotProduct(temp, temp) < lightradius2)
790                                                         loadmodel->surfacevisframes[*mark] = -2;
791                                         }
792                                 }
793                         }
794                 }
795                 // build list of light receiving surfaces
796                 e->numsurfaces = 0;
797                 for (j = 0;j < loadmodel->numsurfaces;j++)
798                         if (loadmodel->surfacevisframes[j] == -2)
799                                 e->numsurfaces++;
800                 e->surfaces = NULL;
801                 if (e->numsurfaces > 0)
802                 {
803                         e->surfaces = Mem_Alloc(loadmodel->mempool, sizeof(msurface_t *) * e->numsurfaces);
804                         e->numsurfaces = 0;
805                         for (j = 0;j < loadmodel->numsurfaces;j++)
806                                 if (loadmodel->surfacevisframes[j] == -2)
807                                         e->surfaces[e->numsurfaces++] = loadmodel->surfaces + j;
808                 }
809                 // find bounding box and sphere of lit surfaces
810                 // (these will be used for creating a shape to clip the light)
811                 radius2 = 0;
812                 for (j = 0;j < e->numsurfaces;j++)
813                 {
814                         surf = e->surfaces[j];
815                         if (j == 0)
816                         {
817                                 VectorCopy(surf->poly_verts, e->mins);
818                                 VectorCopy(surf->poly_verts, e->maxs);
819                         }
820                         for (k = 0, v = surf->poly_verts;k < surf->poly_numverts;k++, v += 3)
821                         {
822                                 if (e->mins[0] > v[0]) e->mins[0] = v[0];if (e->maxs[0] < v[0]) e->maxs[0] = v[0];
823                                 if (e->mins[1] > v[1]) e->mins[1] = v[1];if (e->maxs[1] < v[1]) e->maxs[1] = v[1];
824                                 if (e->mins[2] > v[2]) e->mins[2] = v[2];if (e->maxs[2] < v[2]) e->maxs[2] = v[2];
825                                 VectorSubtract(v, e->origin, temp);
826                                 dist = DotProduct(temp, temp);
827                                 if (radius2 < dist)
828                                         radius2 = dist;
829                         }
830                 }
831                 if (e->cullradius2 > radius2)
832                 {
833                         e->cullradius2 = radius2;
834                         e->cullradius = sqrt(e->cullradius2);
835                 }
836                 if (e->mins[0] < e->origin[0] - e->lightradius) e->mins[0] = e->origin[0] - e->lightradius;
837                 if (e->maxs[0] > e->origin[0] + e->lightradius) e->maxs[0] = e->origin[0] + e->lightradius;
838                 if (e->mins[1] < e->origin[1] - e->lightradius) e->mins[1] = e->origin[1] - e->lightradius;
839                 if (e->maxs[1] > e->origin[1] + e->lightradius) e->maxs[1] = e->origin[1] + e->lightradius;
840                 if (e->mins[2] < e->origin[2] - e->lightradius) e->mins[2] = e->origin[2] - e->lightradius;
841                 if (e->maxs[2] > e->origin[2] + e->lightradius) e->maxs[2] = e->origin[2] + e->lightradius;
842                 // clip shadow volumes against eachother to remove unnecessary
843                 // polygons (and sections of polygons)
844                 {
845                         //vec3_t polymins, polymaxs;
846                         int maxverts = 4;
847                         float *verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
848                         float f, *v0, *v1, projectdistance;
849
850                         e->shadowvolume = Mod_ShadowMesh_Begin(loadmodel->mempool, 1024);
851 #if 0
852                         {
853                         vec3_t outermins, outermaxs, innermins, innermaxs;
854                         innermins[0] = e->mins[0] - 1;
855                         innermins[1] = e->mins[1] - 1;
856                         innermins[2] = e->mins[2] - 1;
857                         innermaxs[0] = e->maxs[0] + 1;
858                         innermaxs[1] = e->maxs[1] + 1;
859                         innermaxs[2] = e->maxs[2] + 1;
860                         outermins[0] = loadmodel->normalmins[0] - 1;
861                         outermins[1] = loadmodel->normalmins[1] - 1;
862                         outermins[2] = loadmodel->normalmins[2] - 1;
863                         outermaxs[0] = loadmodel->normalmaxs[0] + 1;
864                         outermaxs[1] = loadmodel->normalmaxs[1] + 1;
865                         outermaxs[2] = loadmodel->normalmaxs[2] + 1;
866                         // add bounding box around the whole shadow volume set,
867                         // facing inward to limit light area, with an outer bounding box
868                         // facing outward (this is needed by the shadow rendering method)
869                         // X major
870                         verts[ 0] = innermaxs[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
871                         verts[ 3] = innermaxs[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
872                         verts[ 6] = innermaxs[0];verts[ 7] = innermaxs[1];verts[ 8] = innermins[2];
873                         verts[ 9] = innermaxs[0];verts[10] = innermaxs[1];verts[11] = innermaxs[2];
874                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
875                         verts[ 0] = outermaxs[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
876                         verts[ 3] = outermaxs[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
877                         verts[ 6] = outermaxs[0];verts[ 7] = outermins[1];verts[ 8] = outermins[2];
878                         verts[ 9] = outermaxs[0];verts[10] = outermins[1];verts[11] = outermaxs[2];
879                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
880                         // X minor
881                         verts[ 0] = innermins[0];verts[ 1] = innermaxs[1];verts[ 2] = innermaxs[2];
882                         verts[ 3] = innermins[0];verts[ 4] = innermaxs[1];verts[ 5] = innermins[2];
883                         verts[ 6] = innermins[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
884                         verts[ 9] = innermins[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
885                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
886                         verts[ 0] = outermins[0];verts[ 1] = outermins[1];verts[ 2] = outermaxs[2];
887                         verts[ 3] = outermins[0];verts[ 4] = outermins[1];verts[ 5] = outermins[2];
888                         verts[ 6] = outermins[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
889                         verts[ 9] = outermins[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
890                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
891                         // Y major
892                         verts[ 0] = innermaxs[0];verts[ 1] = innermaxs[1];verts[ 2] = innermaxs[2];
893                         verts[ 3] = innermaxs[0];verts[ 4] = innermaxs[1];verts[ 5] = innermins[2];
894                         verts[ 6] = innermins[0];verts[ 7] = innermaxs[1];verts[ 8] = innermins[2];
895                         verts[ 9] = innermins[0];verts[10] = innermaxs[1];verts[11] = innermaxs[2];
896                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
897                         verts[ 0] = outermins[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
898                         verts[ 3] = outermins[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
899                         verts[ 6] = outermaxs[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
900                         verts[ 9] = outermaxs[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
901                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
902                         // Y minor
903                         verts[ 0] = innermins[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
904                         verts[ 3] = innermins[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
905                         verts[ 6] = innermaxs[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
906                         verts[ 9] = innermaxs[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
907                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
908                         verts[ 0] = outermaxs[0];verts[ 1] = outermins[1];verts[ 2] = outermaxs[2];
909                         verts[ 3] = outermaxs[0];verts[ 4] = outermins[1];verts[ 5] = outermins[2];
910                         verts[ 6] = outermins[0];verts[ 7] = outermins[1];verts[ 8] = outermins[2];
911                         verts[ 9] = outermins[0];verts[10] = outermins[1];verts[11] = outermaxs[2];
912                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
913                         // Z major
914                         verts[ 0] = innermaxs[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
915                         verts[ 3] = innermaxs[0];verts[ 4] = innermaxs[1];verts[ 5] = innermaxs[2];
916                         verts[ 6] = innermins[0];verts[ 7] = innermaxs[1];verts[ 8] = innermaxs[2];
917                         verts[ 9] = innermins[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
918                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
919                         verts[ 0] = outermaxs[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
920                         verts[ 3] = outermaxs[0];verts[ 4] = outermins[1];verts[ 5] = outermaxs[2];
921                         verts[ 6] = outermins[0];verts[ 7] = outermins[1];verts[ 8] = outermaxs[2];
922                         verts[ 9] = outermins[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
923                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
924                         // Z minor
925                         verts[ 0] = innermaxs[0];verts[ 1] = innermaxs[1];verts[ 2] = innermins[2];
926                         verts[ 3] = innermaxs[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
927                         verts[ 6] = innermins[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
928                         verts[ 9] = innermins[0];verts[10] = innermaxs[1];verts[11] = innermins[2];
929                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
930                         verts[ 0] = outermaxs[0];verts[ 1] = outermins[1];verts[ 2] = outermins[2];
931                         verts[ 3] = outermaxs[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
932                         verts[ 6] = outermins[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
933                         verts[ 9] = outermins[0];verts[10] = outermins[1];verts[11] = outermins[2];
934                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
935                         }
936 #endif
937                         castshadowcount++;
938                         for (j = 0;j < e->numsurfaces;j++)
939                         {
940                                 surf = e->surfaces[j];
941                                 if (surf->flags & SURF_SHADOWCAST)
942                                         surf->castshadow = castshadowcount;
943                         }
944                         for (j = 0;j < e->numsurfaces;j++)
945                         {
946                                 surf = e->surfaces[j];
947                                 if (surf->castshadow != castshadowcount)
948                                         continue;
949                                 f = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
950                                 if (surf->flags & SURF_PLANEBACK)
951                                         f = -f;
952                                 projectdistance = e->lightradius;
953                                 if (maxverts < surf->poly_numverts)
954                                 {
955                                         maxverts = surf->poly_numverts;
956                                         if (verts)
957                                                 Mem_Free(verts);
958                                         verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
959                                 }
960                                 // copy the original polygon, for the front cap of the volume
961                                 for (k = 0, v0 = surf->poly_verts, v1 = verts;k < surf->poly_numverts;k++, v0 += 3, v1 += 3)
962                                         VectorCopy(v0, v1);
963                                 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, surf->poly_numverts, verts);
964                                 // project the original polygon, reversed, for the back cap of the volume
965                                 for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = verts;k < surf->poly_numverts;k++, v0 -= 3, v1 += 3)
966                                 {
967                                         VectorSubtract(v0, e->origin, temp);
968                                         VectorNormalize(temp);
969                                         VectorMA(v0, projectdistance, temp, v1);
970                                 }
971                                 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, surf->poly_numverts, verts);
972                                 // project the shadow volume sides
973                                 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)
974                                 {
975                                         if (!surf->neighborsurfaces[l] || surf->neighborsurfaces[l]->castshadow != castshadowcount)
976                                         {
977                                                 VectorCopy(v1, &verts[0]);
978                                                 VectorCopy(v0, &verts[3]);
979                                                 VectorCopy(v0, &verts[6]);
980                                                 VectorCopy(v1, &verts[9]);
981                                                 VectorSubtract(&verts[6], e->origin, temp);
982                                                 VectorNormalize(temp);
983                                                 VectorMA(&verts[6], projectdistance, temp, &verts[6]);
984                                                 VectorSubtract(&verts[9], e->origin, temp);
985                                                 VectorNormalize(temp);
986                                                 VectorMA(&verts[9], projectdistance, temp, &verts[9]);
987                                                 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
988                                         }
989                                 }
990                         }
991                         // build the triangle mesh
992                         e->shadowvolume = Mod_ShadowMesh_Finish(loadmodel->mempool, e->shadowvolume);
993                         {
994                                 shadowmesh_t *mesh;
995                                 l = 0;
996                                 for (mesh = e->shadowvolume;mesh;mesh = mesh->next)
997                                         l += mesh->numtriangles;
998                                 Con_Printf("light %i shadow volume built containing %i triangles\n", lnum, l);
999                         }
1000                 }
1001         }
1002 }
1003 */
1004
1005
1006 /*
1007 =================
1008 Mod_LoadVisibility
1009 =================
1010 */
1011 static void Mod_LoadVisibility (lump_t *l)
1012 {
1013         loadmodel->visdata = NULL;
1014         if (!l->filelen)
1015                 return;
1016         loadmodel->visdata = Mem_Alloc(loadmodel->mempool, l->filelen);
1017         memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
1018 }
1019
1020 // used only for HalfLife maps
1021 void Mod_ParseWadsFromEntityLump(const char *data)
1022 {
1023         char key[128], value[4096];
1024         char wadname[128];
1025         int i, j, k;
1026         if (!data)
1027                 return;
1028         if (!COM_ParseToken(&data))
1029                 return; // error
1030         if (com_token[0] != '{')
1031                 return; // error
1032         while (1)
1033         {
1034                 if (!COM_ParseToken(&data))
1035                         return; // error
1036                 if (com_token[0] == '}')
1037                         break; // end of worldspawn
1038                 if (com_token[0] == '_')
1039                         strcpy(key, com_token + 1);
1040                 else
1041                         strcpy(key, com_token);
1042                 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1043                         key[strlen(key)-1] = 0;
1044                 if (!COM_ParseToken(&data))
1045                         return; // error
1046                 strcpy(value, com_token);
1047                 if (!strcmp("wad", key)) // for HalfLife maps
1048                 {
1049                         if (loadmodel->ishlbsp)
1050                         {
1051                                 j = 0;
1052                                 for (i = 0;i < 4096;i++)
1053                                         if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1054                                                 break;
1055                                 if (value[i])
1056                                 {
1057                                         for (;i < 4096;i++)
1058                                         {
1059                                                 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1060                                                 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1061                                                         j = i+1;
1062                                                 else if (value[i] == ';' || value[i] == 0)
1063                                                 {
1064                                                         k = value[i];
1065                                                         value[i] = 0;
1066                                                         strcpy(wadname, "textures/");
1067                                                         strcat(wadname, &value[j]);
1068                                                         W_LoadTextureWadFile (wadname, false);
1069                                                         j = i+1;
1070                                                         if (!k)
1071                                                                 break;
1072                                                 }
1073                                         }
1074                                 }
1075                         }
1076                 }
1077         }
1078 }
1079
1080 /*
1081 =================
1082 Mod_LoadEntities
1083 =================
1084 */
1085 static void Mod_LoadEntities (lump_t *l)
1086 {
1087         loadmodel->entities = NULL;
1088         if (!l->filelen)
1089                 return;
1090         loadmodel->entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1091         memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
1092         if (loadmodel->ishlbsp)
1093                 Mod_ParseWadsFromEntityLump(loadmodel->entities);
1094 }
1095
1096
1097 /*
1098 =================
1099 Mod_LoadVertexes
1100 =================
1101 */
1102 static void Mod_LoadVertexes (lump_t *l)
1103 {
1104         dvertex_t       *in;
1105         mvertex_t       *out;
1106         int                     i, count;
1107
1108         in = (void *)(mod_base + l->fileofs);
1109         if (l->filelen % sizeof(*in))
1110                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1111         count = l->filelen / sizeof(*in);
1112         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1113
1114         loadmodel->vertexes = out;
1115         loadmodel->numvertexes = count;
1116
1117         for ( i=0 ; i<count ; i++, in++, out++)
1118         {
1119                 out->position[0] = LittleFloat (in->point[0]);
1120                 out->position[1] = LittleFloat (in->point[1]);
1121                 out->position[2] = LittleFloat (in->point[2]);
1122         }
1123 }
1124
1125 /*
1126 =================
1127 Mod_LoadSubmodels
1128 =================
1129 */
1130 static void Mod_LoadSubmodels (lump_t *l)
1131 {
1132         dmodel_t        *in;
1133         dmodel_t        *out;
1134         int                     i, j, count;
1135
1136         in = (void *)(mod_base + l->fileofs);
1137         if (l->filelen % sizeof(*in))
1138                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1139         count = l->filelen / sizeof(*in);
1140         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1141
1142         loadmodel->submodels = out;
1143         loadmodel->numsubmodels = count;
1144
1145         for ( i=0 ; i<count ; i++, in++, out++)
1146         {
1147                 for (j=0 ; j<3 ; j++)
1148                 {
1149                         // spread the mins / maxs by a pixel
1150                         out->mins[j] = LittleFloat (in->mins[j]) - 1;
1151                         out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
1152                         out->origin[j] = LittleFloat (in->origin[j]);
1153                 }
1154                 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1155                         out->headnode[j] = LittleLong (in->headnode[j]);
1156                 out->visleafs = LittleLong (in->visleafs);
1157                 out->firstface = LittleLong (in->firstface);
1158                 out->numfaces = LittleLong (in->numfaces);
1159         }
1160 }
1161
1162 /*
1163 =================
1164 Mod_LoadEdges
1165 =================
1166 */
1167 static void Mod_LoadEdges (lump_t *l)
1168 {
1169         dedge_t *in;
1170         medge_t *out;
1171         int     i, count;
1172
1173         in = (void *)(mod_base + l->fileofs);
1174         if (l->filelen % sizeof(*in))
1175                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1176         count = l->filelen / sizeof(*in);
1177         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1178
1179         loadmodel->edges = out;
1180         loadmodel->numedges = count;
1181
1182         for ( i=0 ; i<count ; i++, in++, out++)
1183         {
1184                 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1185                 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1186         }
1187 }
1188
1189 /*
1190 =================
1191 Mod_LoadTexinfo
1192 =================
1193 */
1194 static void Mod_LoadTexinfo (lump_t *l)
1195 {
1196         texinfo_t *in;
1197         mtexinfo_t *out;
1198         int i, j, k, count, miptex;
1199
1200         in = (void *)(mod_base + l->fileofs);
1201         if (l->filelen % sizeof(*in))
1202                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1203         count = l->filelen / sizeof(*in);
1204         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1205
1206         loadmodel->texinfo = out;
1207         loadmodel->numtexinfo = count;
1208
1209         for (i = 0;i < count;i++, in++, out++)
1210         {
1211                 for (k = 0;k < 2;k++)
1212                         for (j = 0;j < 4;j++)
1213                                 out->vecs[k][j] = LittleFloat (in->vecs[k][j]);
1214
1215                 miptex = LittleLong (in->miptex);
1216                 out->flags = LittleLong (in->flags);
1217
1218                 out->texture = NULL;
1219                 if (loadmodel->textures)
1220                 {
1221                         if ((unsigned int) miptex >= (unsigned int) loadmodel->numtextures)
1222                                 Con_Printf ("error in model \"%s\": invalid miptex index %i (of %i)\n", loadmodel->name, miptex, loadmodel->numtextures);
1223                         else
1224                                 out->texture = loadmodel->textures + miptex;
1225                 }
1226                 if (out->flags & TEX_SPECIAL)
1227                 {
1228                         // if texture chosen is NULL or the shader needs a lightmap,
1229                         // force to notexture water shader
1230                         if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
1231                                 out->texture = loadmodel->textures + (loadmodel->numtextures - 1);
1232                 }
1233                 else
1234                 {
1235                         // if texture chosen is NULL, force to notexture
1236                         if (out->texture == NULL)
1237                                 out->texture = loadmodel->textures + (loadmodel->numtextures - 2);
1238                 }
1239         }
1240 }
1241
1242 /*
1243 ================
1244 CalcSurfaceExtents
1245
1246 Fills in s->texturemins[] and s->extents[]
1247 ================
1248 */
1249 static void CalcSurfaceExtents (msurface_t *s)
1250 {
1251         float   mins[2], maxs[2], val;
1252         int             i,j, e;
1253         mvertex_t       *v;
1254         mtexinfo_t      *tex;
1255         int             bmins[2], bmaxs[2];
1256
1257         mins[0] = mins[1] = 999999999;
1258         maxs[0] = maxs[1] = -999999999;
1259
1260         tex = s->texinfo;
1261
1262         for (i=0 ; i<s->numedges ; i++)
1263         {
1264                 e = loadmodel->surfedges[s->firstedge+i];
1265                 if (e >= 0)
1266                         v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
1267                 else
1268                         v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
1269
1270                 for (j=0 ; j<2 ; j++)
1271                 {
1272                         val = v->position[0] * tex->vecs[j][0] +
1273                                 v->position[1] * tex->vecs[j][1] +
1274                                 v->position[2] * tex->vecs[j][2] +
1275                                 tex->vecs[j][3];
1276                         if (val < mins[j])
1277                                 mins[j] = val;
1278                         if (val > maxs[j])
1279                                 maxs[j] = val;
1280                 }
1281         }
1282
1283         for (i=0 ; i<2 ; i++)
1284         {
1285                 bmins[i] = floor(mins[i]/16);
1286                 bmaxs[i] = ceil(maxs[i]/16);
1287
1288                 s->texturemins[i] = bmins[i] * 16;
1289                 s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
1290         }
1291 }
1292
1293
1294 void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs)
1295 {
1296         int             i, j;
1297         float   *v;
1298
1299         mins[0] = mins[1] = mins[2] = 9999;
1300         maxs[0] = maxs[1] = maxs[2] = -9999;
1301         v = verts;
1302         for (i = 0;i < numverts;i++)
1303         {
1304                 for (j = 0;j < 3;j++, v++)
1305                 {
1306                         if (*v < mins[j])
1307                                 mins[j] = *v;
1308                         if (*v > maxs[j])
1309                                 maxs[j] = *v;
1310                 }
1311         }
1312 }
1313
1314 #if 0
1315 #define MAX_SUBDIVPOLYTRIANGLES 4096
1316 #define MAX_SUBDIVPOLYVERTS (MAX_SUBDIVPOLYTRIANGLES * 3)
1317
1318 static int subdivpolyverts, subdivpolytriangles;
1319 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1320 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1321
1322 static int subdivpolylookupvert(vec3_t v)
1323 {
1324         int i;
1325         for (i = 0;i < subdivpolyverts;i++)
1326                 if (subdivpolyvert[i][0] == v[0]
1327                  && subdivpolyvert[i][1] == v[1]
1328                  && subdivpolyvert[i][2] == v[2])
1329                         return i;
1330         if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1331                 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1332         VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1333         return subdivpolyverts++;
1334 }
1335
1336 static void SubdividePolygon (int numverts, float *verts)
1337 {
1338         int             i, i1, i2, i3, f, b, c, p;
1339         vec3_t  mins, maxs, front[256], back[256];
1340         float   m, *pv, *cv, dist[256], frac;
1341
1342         if (numverts > 250)
1343                 Host_Error ("SubdividePolygon: ran out of verts in buffer");
1344
1345         BoundPoly (numverts, verts, mins, maxs);
1346
1347         for (i = 0;i < 3;i++)
1348         {
1349                 m = (mins[i] + maxs[i]) * 0.5;
1350                 m = r_subdivide_size.value * floor (m/r_subdivide_size.value + 0.5);
1351                 if (maxs[i] - m < 8)
1352                         continue;
1353                 if (m - mins[i] < 8)
1354                         continue;
1355
1356                 // cut it
1357                 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1358                         dist[c] = cv[i] - m;
1359
1360                 f = b = 0;
1361                 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1362                 {
1363                         if (dist[p] >= 0)
1364                         {
1365                                 VectorCopy (pv, front[f]);
1366                                 f++;
1367                         }
1368                         if (dist[p] <= 0)
1369                         {
1370                                 VectorCopy (pv, back[b]);
1371                                 b++;
1372                         }
1373                         if (dist[p] == 0 || dist[c] == 0)
1374                                 continue;
1375                         if ( (dist[p] > 0) != (dist[c] > 0) )
1376                         {
1377                                 // clip point
1378                                 frac = dist[p] / (dist[p] - dist[c]);
1379                                 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1380                                 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1381                                 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1382                                 f++;
1383                                 b++;
1384                         }
1385                 }
1386
1387                 SubdividePolygon (f, front[0]);
1388                 SubdividePolygon (b, back[0]);
1389                 return;
1390         }
1391
1392         i1 = subdivpolylookupvert(verts);
1393         i2 = subdivpolylookupvert(verts + 3);
1394         for (i = 2;i < numverts;i++)
1395         {
1396                 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1397                 {
1398                         Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1399                         return;
1400                 }
1401
1402                 i3 = subdivpolylookupvert(verts + i * 3);
1403                 subdivpolyindex[subdivpolytriangles][0] = i1;
1404                 subdivpolyindex[subdivpolytriangles][1] = i2;
1405                 subdivpolyindex[subdivpolytriangles][2] = i3;
1406                 i2 = i3;
1407                 subdivpolytriangles++;
1408         }
1409 }
1410
1411 /*
1412 ================
1413 Mod_GenerateWarpMesh
1414
1415 Breaks a polygon up along axial 64 unit
1416 boundaries so that turbulent and sky warps
1417 can be done reasonably.
1418 ================
1419 */
1420 void Mod_GenerateWarpMesh (msurface_t *surf)
1421 {
1422         int i, j;
1423         surfvertex_t *v;
1424         surfmesh_t *mesh;
1425
1426         subdivpolytriangles = 0;
1427         subdivpolyverts = 0;
1428         SubdividePolygon (surf->poly_numverts, surf->poly_verts);
1429         if (subdivpolytriangles < 1)
1430                 Host_Error("Mod_GenerateWarpMesh: no triangles?\n");
1431
1432         surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1433         mesh->numverts = subdivpolyverts;
1434         mesh->numtriangles = subdivpolytriangles;
1435         mesh->vertex = (surfvertex_t *)(mesh + 1);
1436         mesh->index = (int *)(mesh->vertex + mesh->numverts);
1437         memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1438
1439         for (i = 0;i < mesh->numtriangles;i++)
1440                 for (j = 0;j < 3;j++)
1441                         mesh->index[i*3+j] = subdivpolyindex[i][j];
1442
1443         for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1444         {
1445                 VectorCopy(subdivpolyvert[i], v->v);
1446                 v->st[0] = DotProduct (v->v, surf->texinfo->vecs[0]);
1447                 v->st[1] = DotProduct (v->v, surf->texinfo->vecs[1]);
1448         }
1449 }
1450 #endif
1451
1452 surfmesh_t *Mod_AllocSurfMesh(int numverts, int numtriangles)
1453 {
1454         surfmesh_t *mesh;
1455         mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (4 + 4 + 4 + 4 + 4 + 4 + 4 + 1) * sizeof(float));
1456         mesh->numverts = numverts;
1457         mesh->numtriangles = numtriangles;
1458         mesh->verts = (float *)(mesh + 1);
1459         mesh->str = mesh->verts + mesh->numverts * 4;
1460         mesh->uvw = mesh->str + mesh->numverts * 4;
1461         mesh->abc = mesh->uvw + mesh->numverts * 4;
1462         mesh->svectors = (float *)(mesh->abc + mesh->numverts * 4);
1463         mesh->tvectors = mesh->svectors + mesh->numverts * 4;
1464         mesh->normals = mesh->tvectors + mesh->numverts * 4;
1465         mesh->lightmapoffsets = (int *)(mesh->normals + mesh->numverts * 4);
1466         mesh->index = mesh->lightmapoffsets + mesh->numverts;
1467         mesh->triangleneighbors = mesh->index + mesh->numtriangles * 3;
1468         return mesh;
1469 }
1470
1471 void Mod_GenerateWallMesh (msurface_t *surf, int vertexonly)
1472 {
1473         int i, iu, iv, *index, smax, tmax;
1474         float *in, s, t, u, v, ubase, vbase, uscale, vscale, normal[3];
1475         surfmesh_t *mesh;
1476
1477         smax = surf->extents[0] >> 4;
1478         tmax = surf->extents[1] >> 4;
1479
1480         if (vertexonly)
1481         {
1482                 surf->lightmaptexturestride = 0;
1483                 surf->lightmaptexture = NULL;
1484                 uscale = 0;
1485                 vscale = 0;
1486                 ubase = 0;
1487                 vbase = 0;
1488         }
1489         else
1490         {
1491                 surf->flags |= SURF_LIGHTMAP;
1492                 if (r_miplightmaps.integer)
1493                 {
1494                         surf->lightmaptexturestride = (surf->extents[0]>>4)+1;
1495                         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);
1496                 }
1497                 else
1498                 {
1499                         surf->lightmaptexturestride = R_CompatibleFragmentWidth((surf->extents[0]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1500                         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);
1501                 }
1502                 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1503                 uscale = (uscale - ubase) * 16.0 / ((surf->extents[0] & ~15) + 16);
1504                 vscale = (vscale - vbase) * 16.0 / ((surf->extents[1] & ~15) + 16);
1505         }
1506
1507         surf->mesh = mesh = Mod_AllocSurfMesh(surf->poly_numverts, surf->poly_numverts - 2);
1508
1509         index = mesh->index;
1510         for (i = 0;i < mesh->numtriangles;i++)
1511         {
1512                 *index++ = 0;
1513                 *index++ = i + 1;
1514                 *index++ = i + 2;
1515         }
1516         Mod_BuildTriangleNeighbors(mesh->triangleneighbors, mesh->index, mesh->numtriangles);
1517
1518         VectorCopy(surf->plane->normal, normal);
1519         if (surf->flags & SURF_PLANEBACK)
1520                 VectorNegate(normal, normal);
1521         for (i = 0, in = surf->poly_verts;i < mesh->numverts;i++, in += 3)
1522         {
1523                 s = DotProduct (in, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1524                 t = DotProduct (in, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1525                 u = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1526                 v = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1527                 // LordHavoc: calc lightmap data offset for vertex lighting to use
1528                 iu = (int) u;
1529                 iv = (int) v;
1530                 iu = bound(0, iu, smax);
1531                 iv = bound(0, iv, tmax);
1532                 u = u * uscale + ubase;
1533                 v = v * vscale + vbase;
1534
1535                 mesh->verts[i * 4 + 0] = in[0];
1536                 mesh->verts[i * 4 + 1] = in[1];
1537                 mesh->verts[i * 4 + 2] = in[2];
1538                 mesh->str[i * 4 + 0] = s / surf->texinfo->texture->width;
1539                 mesh->str[i * 4 + 1] = t / surf->texinfo->texture->height;
1540                 mesh->uvw[i * 4 + 0] = u;
1541                 mesh->uvw[i * 4 + 1] = v;
1542                 mesh->abc[i * 4 + 0] = s * (1.0f / 16.0f);
1543                 mesh->abc[i * 4 + 1] = t * (1.0f / 16.0f);
1544                 mesh->lightmapoffsets[i] = ((iv * (smax+1) + iu) * 3);
1545         }
1546         Mod_BuildTextureVectorsAndNormals(mesh->numverts, mesh->numtriangles, mesh->verts, mesh->str, mesh->index, mesh->svectors, mesh->tvectors, mesh->normals);
1547 }
1548
1549 void Mod_GenerateVertexMesh (msurface_t *surf)
1550 {
1551         int i, *index;
1552         float *in, s, t, normal[3];
1553         surfmesh_t *mesh;
1554
1555         surf->lightmaptexturestride = 0;
1556         surf->lightmaptexture = NULL;
1557
1558         surf->mesh = mesh = Mod_AllocSurfMesh(surf->poly_numverts, surf->poly_numverts - 2);
1559
1560         index = mesh->index;
1561         for (i = 0;i < mesh->numtriangles;i++)
1562         {
1563                 *index++ = 0;
1564                 *index++ = i + 1;
1565                 *index++ = i + 2;
1566         }
1567         Mod_BuildTriangleNeighbors(mesh->triangleneighbors, mesh->index, mesh->numtriangles);
1568
1569         VectorCopy(surf->plane->normal, normal);
1570         if (surf->flags & SURF_PLANEBACK)
1571                 VectorNegate(normal, normal);
1572         for (i = 0, in = surf->poly_verts;i < mesh->numverts;i++, in += 3)
1573         {
1574                 s = (DotProduct (in, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]);
1575                 t = (DotProduct (in, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]);
1576                 mesh->verts[i * 4 + 0] = in[0];
1577                 mesh->verts[i * 4 + 1] = in[1];
1578                 mesh->verts[i * 4 + 2] = in[2];
1579                 mesh->str[i * 4 + 0] = s / surf->texinfo->texture->width;
1580                 mesh->str[i * 4 + 1] = t / surf->texinfo->texture->height;
1581                 mesh->uvw[i * 4 + 0] = 0;
1582                 mesh->uvw[i * 4 + 1] = 0;
1583                 mesh->abc[i * 4 + 0] = s * (1.0f / 16.0f);
1584                 mesh->abc[i * 4 + 1] = t * (1.0f / 16.0f);
1585         }
1586         Mod_BuildTextureVectorsAndNormals(mesh->numverts, mesh->numtriangles, mesh->verts, mesh->str, mesh->index, mesh->svectors, mesh->tvectors, mesh->normals);
1587 }
1588
1589 void Mod_GenerateSurfacePolygon (msurface_t *surf)
1590 {
1591         int i, lindex;
1592         float *vec, *vert, mins[3], maxs[3];
1593
1594         // convert edges back to a normal polygon
1595         surf->poly_numverts = surf->numedges;
1596         vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges);
1597         for (i = 0;i < surf->numedges;i++)
1598         {
1599                 lindex = loadmodel->surfedges[surf->firstedge + i];
1600                 if (lindex > 0)
1601                         vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
1602                 else
1603                         vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
1604                 VectorCopy (vec, vert);
1605                 vert += 3;
1606         }
1607         vert = surf->poly_verts;
1608         VectorCopy(vert, mins);
1609         VectorCopy(vert, maxs);
1610         vert += 3;
1611         for (i = 1;i < surf->poly_numverts;i++, vert += 3)
1612         {
1613                 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1614                 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1615                 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1616         }
1617         VectorCopy(mins, surf->poly_mins);
1618         VectorCopy(maxs, surf->poly_maxs);
1619         surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1620         surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1621         surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1622 }
1623
1624 /*
1625 =================
1626 Mod_LoadFaces
1627 =================
1628 */
1629 static void Mod_LoadFaces (lump_t *l)
1630 {
1631         dface_t *in;
1632         msurface_t      *out;
1633         int i, count, surfnum, planenum, ssize, tsize;
1634
1635         in = (void *)(mod_base + l->fileofs);
1636         if (l->filelen % sizeof(*in))
1637                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1638         count = l->filelen / sizeof(*in);
1639         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1640
1641         loadmodel->surfaces = out;
1642         loadmodel->numsurfaces = count;
1643         loadmodel->surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1644         loadmodel->surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1645         loadmodel->pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1646
1647         for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
1648         {
1649                 out->number = surfnum;
1650                 // FIXME: validate edges, texinfo, etc?
1651                 out->firstedge = LittleLong(in->firstedge);
1652                 out->numedges = LittleShort(in->numedges);
1653                 if ((unsigned int) out->firstedge + (unsigned int) out->numedges > (unsigned int) loadmodel->numsurfedges)
1654                         Host_Error("Mod_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", out->firstedge, out->numedges, loadmodel->numsurfedges);
1655
1656                 i = LittleShort (in->texinfo);
1657                 if ((unsigned int) i >= (unsigned int) loadmodel->numtexinfo)
1658                         Host_Error("Mod_LoadFaces: invalid texinfo index %i (model has %i texinfos)\n", i, loadmodel->numtexinfo);
1659                 out->texinfo = loadmodel->texinfo + i;
1660                 out->flags = out->texinfo->texture->flags;
1661
1662                 planenum = LittleShort(in->planenum);
1663                 if ((unsigned int) planenum >= (unsigned int) loadmodel->numplanes)
1664                         Host_Error("Mod_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->numplanes);
1665
1666                 if (LittleShort(in->side))
1667                         out->flags |= SURF_PLANEBACK;
1668
1669                 out->plane = loadmodel->planes + planenum;
1670
1671                 // clear lightmap (filled in later)
1672                 out->lightmaptexture = NULL;
1673
1674                 // force lightmap upload on first time seeing the surface
1675                 out->cached_dlight = true;
1676
1677                 CalcSurfaceExtents (out);
1678
1679                 ssize = (out->extents[0] >> 4) + 1;
1680                 tsize = (out->extents[1] >> 4) + 1;
1681
1682                 // lighting info
1683                 for (i = 0;i < MAXLIGHTMAPS;i++)
1684                         out->styles[i] = in->styles[i];
1685                 i = LittleLong(in->lightofs);
1686                 if (i == -1)
1687                         out->samples = NULL;
1688                 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1689                         out->samples = loadmodel->lightdata + i;
1690                 else // LordHavoc: white lighting (bsp version 29)
1691                         out->samples = loadmodel->lightdata + (i * 3);
1692
1693                 Mod_GenerateSurfacePolygon(out);
1694                 if (out->texinfo->texture->shader == &Cshader_wall_lightmap)
1695                 {
1696                         if ((out->extents[0] >> 4) + 1 > (256) || (out->extents[1] >> 4) + 1 > (256))
1697                                 Host_Error ("Bad surface extents");
1698                         Mod_GenerateWallMesh (out, false);
1699                         // stainmap for permanent marks on walls
1700                         out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1701                         // clear to white
1702                         memset(out->stainsamples, 255, ssize * tsize * 3);
1703                 }
1704                 else
1705                         Mod_GenerateVertexMesh (out);
1706         }
1707 }
1708
1709 /*
1710 =================
1711 Mod_SetParent
1712 =================
1713 */
1714 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1715 {
1716         node->parent = parent;
1717         if (node->contents < 0)
1718                 return;
1719         Mod_SetParent (node->children[0], node);
1720         Mod_SetParent (node->children[1], node);
1721 }
1722
1723 /*
1724 =================
1725 Mod_LoadNodes
1726 =================
1727 */
1728 static void Mod_LoadNodes (lump_t *l)
1729 {
1730         int                     i, j, count, p;
1731         dnode_t         *in;
1732         mnode_t         *out;
1733
1734         in = (void *)(mod_base + l->fileofs);
1735         if (l->filelen % sizeof(*in))
1736                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1737         count = l->filelen / sizeof(*in);
1738         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1739
1740         loadmodel->nodes = out;
1741         loadmodel->numnodes = count;
1742
1743         for ( i=0 ; i<count ; i++, in++, out++)
1744         {
1745                 for (j=0 ; j<3 ; j++)
1746                 {
1747                         out->mins[j] = LittleShort (in->mins[j]);
1748                         out->maxs[j] = LittleShort (in->maxs[j]);
1749                 }
1750
1751                 p = LittleLong(in->planenum);
1752                 out->plane = loadmodel->planes + p;
1753
1754                 out->firstsurface = LittleShort (in->firstface);
1755                 out->numsurfaces = LittleShort (in->numfaces);
1756
1757                 for (j=0 ; j<2 ; j++)
1758                 {
1759                         p = LittleShort (in->children[j]);
1760                         if (p >= 0)
1761                                 out->children[j] = loadmodel->nodes + p;
1762                         else
1763                                 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1764                 }
1765         }
1766
1767         Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1768 }
1769
1770 /*
1771 =================
1772 Mod_LoadLeafs
1773 =================
1774 */
1775 static void Mod_LoadLeafs (lump_t *l)
1776 {
1777         dleaf_t         *in;
1778         mleaf_t         *out;
1779         int                     i, j, count, p;
1780
1781         in = (void *)(mod_base + l->fileofs);
1782         if (l->filelen % sizeof(*in))
1783                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1784         count = l->filelen / sizeof(*in);
1785         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1786
1787         loadmodel->leafs = out;
1788         loadmodel->numleafs = count;
1789
1790         for ( i=0 ; i<count ; i++, in++, out++)
1791         {
1792                 for (j=0 ; j<3 ; j++)
1793                 {
1794                         out->mins[j] = LittleShort (in->mins[j]);
1795                         out->maxs[j] = LittleShort (in->maxs[j]);
1796                 }
1797
1798                 p = LittleLong(in->contents);
1799                 out->contents = p;
1800
1801                 out->firstmarksurface = loadmodel->marksurfaces +
1802                         LittleShort(in->firstmarksurface);
1803                 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1804
1805                 p = LittleLong(in->visofs);
1806                 if (p == -1)
1807                         out->compressed_vis = NULL;
1808                 else
1809                         out->compressed_vis = loadmodel->visdata + p;
1810
1811                 for (j=0 ; j<4 ; j++)
1812                         out->ambient_sound_level[j] = in->ambient_level[j];
1813
1814                 // FIXME: Insert caustics here
1815         }
1816 }
1817
1818 /*
1819 =================
1820 Mod_LoadClipnodes
1821 =================
1822 */
1823 static void Mod_LoadClipnodes (lump_t *l)
1824 {
1825         dclipnode_t *in, *out;
1826         int                     i, count;
1827         hull_t          *hull;
1828
1829         in = (void *)(mod_base + l->fileofs);
1830         if (l->filelen % sizeof(*in))
1831                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1832         count = l->filelen / sizeof(*in);
1833         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1834
1835         loadmodel->clipnodes = out;
1836         loadmodel->numclipnodes = count;
1837
1838         if (loadmodel->ishlbsp)
1839         {
1840                 hull = &loadmodel->hulls[1];
1841                 hull->clipnodes = out;
1842                 hull->firstclipnode = 0;
1843                 hull->lastclipnode = count-1;
1844                 hull->planes = loadmodel->planes;
1845                 hull->clip_mins[0] = -16;
1846                 hull->clip_mins[1] = -16;
1847                 hull->clip_mins[2] = -36;
1848                 hull->clip_maxs[0] = 16;
1849                 hull->clip_maxs[1] = 16;
1850                 hull->clip_maxs[2] = 36;
1851                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1852
1853                 hull = &loadmodel->hulls[2];
1854                 hull->clipnodes = out;
1855                 hull->firstclipnode = 0;
1856                 hull->lastclipnode = count-1;
1857                 hull->planes = loadmodel->planes;
1858                 hull->clip_mins[0] = -32;
1859                 hull->clip_mins[1] = -32;
1860                 hull->clip_mins[2] = -32;
1861                 hull->clip_maxs[0] = 32;
1862                 hull->clip_maxs[1] = 32;
1863                 hull->clip_maxs[2] = 32;
1864                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1865
1866                 hull = &loadmodel->hulls[3];
1867                 hull->clipnodes = out;
1868                 hull->firstclipnode = 0;
1869                 hull->lastclipnode = count-1;
1870                 hull->planes = loadmodel->planes;
1871                 hull->clip_mins[0] = -16;
1872                 hull->clip_mins[1] = -16;
1873                 hull->clip_mins[2] = -18;
1874                 hull->clip_maxs[0] = 16;
1875                 hull->clip_maxs[1] = 16;
1876                 hull->clip_maxs[2] = 18;
1877                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1878         }
1879         else
1880         {
1881                 hull = &loadmodel->hulls[1];
1882                 hull->clipnodes = out;
1883                 hull->firstclipnode = 0;
1884                 hull->lastclipnode = count-1;
1885                 hull->planes = loadmodel->planes;
1886                 hull->clip_mins[0] = -16;
1887                 hull->clip_mins[1] = -16;
1888                 hull->clip_mins[2] = -24;
1889                 hull->clip_maxs[0] = 16;
1890                 hull->clip_maxs[1] = 16;
1891                 hull->clip_maxs[2] = 32;
1892                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1893
1894                 hull = &loadmodel->hulls[2];
1895                 hull->clipnodes = out;
1896                 hull->firstclipnode = 0;
1897                 hull->lastclipnode = count-1;
1898                 hull->planes = loadmodel->planes;
1899                 hull->clip_mins[0] = -32;
1900                 hull->clip_mins[1] = -32;
1901                 hull->clip_mins[2] = -24;
1902                 hull->clip_maxs[0] = 32;
1903                 hull->clip_maxs[1] = 32;
1904                 hull->clip_maxs[2] = 64;
1905                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1906         }
1907
1908         for (i=0 ; i<count ; i++, out++, in++)
1909         {
1910                 out->planenum = LittleLong(in->planenum);
1911                 out->children[0] = LittleShort(in->children[0]);
1912                 out->children[1] = LittleShort(in->children[1]);
1913                 if (out->children[0] >= count || out->children[1] >= count)
1914                         Host_Error("Corrupt clipping hull (out of range child)\n");
1915         }
1916 }
1917
1918 /*
1919 =================
1920 Mod_MakeHull0
1921
1922 Duplicate the drawing hull structure as a clipping hull
1923 =================
1924 */
1925 static void Mod_MakeHull0 (void)
1926 {
1927         mnode_t         *in;
1928         dclipnode_t *out;
1929         int                     i;
1930         hull_t          *hull;
1931
1932         hull = &loadmodel->hulls[0];
1933
1934         in = loadmodel->nodes;
1935         out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1936
1937         hull->clipnodes = out;
1938         hull->firstclipnode = 0;
1939         hull->lastclipnode = loadmodel->numnodes - 1;
1940         hull->planes = loadmodel->planes;
1941
1942         for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1943         {
1944                 out->planenum = in->plane - loadmodel->planes;
1945                 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1946                 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1947         }
1948 }
1949
1950 /*
1951 =================
1952 Mod_LoadMarksurfaces
1953 =================
1954 */
1955 static void Mod_LoadMarksurfaces (lump_t *l)
1956 {
1957         int i, j;
1958         short *in;
1959
1960         in = (void *)(mod_base + l->fileofs);
1961         if (l->filelen % sizeof(*in))
1962                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1963         loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1964         loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(int));
1965
1966         for (i = 0;i < loadmodel->nummarksurfaces;i++)
1967         {
1968                 j = (unsigned) LittleShort(in[i]);
1969                 if (j >= loadmodel->numsurfaces)
1970                         Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1971                 loadmodel->marksurfaces[i] = j;
1972         }
1973 }
1974
1975 /*
1976 =================
1977 Mod_LoadSurfedges
1978 =================
1979 */
1980 static void Mod_LoadSurfedges (lump_t *l)
1981 {
1982         int             i;
1983         int             *in;
1984
1985         in = (void *)(mod_base + l->fileofs);
1986         if (l->filelen % sizeof(*in))
1987                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1988         loadmodel->numsurfedges = l->filelen / sizeof(*in);
1989         loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1990
1991         for (i = 0;i < loadmodel->numsurfedges;i++)
1992                 loadmodel->surfedges[i] = LittleLong (in[i]);
1993 }
1994
1995
1996 /*
1997 =================
1998 Mod_LoadPlanes
1999 =================
2000 */
2001 static void Mod_LoadPlanes (lump_t *l)
2002 {
2003         int                     i;
2004         mplane_t        *out;
2005         dplane_t        *in;
2006
2007         in = (void *)(mod_base + l->fileofs);
2008         if (l->filelen % sizeof(*in))
2009                 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
2010
2011         loadmodel->numplanes = l->filelen / sizeof(*in);
2012         loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
2013
2014         for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
2015         {
2016                 out->normal[0] = LittleFloat (in->normal[0]);
2017                 out->normal[1] = LittleFloat (in->normal[1]);
2018                 out->normal[2] = LittleFloat (in->normal[2]);
2019                 out->dist = LittleFloat (in->dist);
2020
2021                 PlaneClassify(out);
2022         }
2023 }
2024
2025 #define MAX_POINTS_ON_WINDING 64
2026
2027 typedef struct
2028 {
2029         int numpoints;
2030         int padding;
2031         double points[8][3]; // variable sized
2032 }
2033 winding_t;
2034
2035 /*
2036 ==================
2037 NewWinding
2038 ==================
2039 */
2040 static winding_t *NewWinding (int points)
2041 {
2042         winding_t *w;
2043         int size;
2044
2045         if (points > MAX_POINTS_ON_WINDING)
2046                 Sys_Error("NewWinding: too many points\n");
2047
2048         size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
2049         w = Mem_Alloc(loadmodel->mempool, size);
2050         memset (w, 0, size);
2051
2052         return w;
2053 }
2054
2055 static void FreeWinding (winding_t *w)
2056 {
2057         Mem_Free(w);
2058 }
2059
2060 /*
2061 =================
2062 BaseWindingForPlane
2063 =================
2064 */
2065 static winding_t *BaseWindingForPlane (mplane_t *p)
2066 {
2067         double org[3], vright[3], vup[3], normal[3];
2068         winding_t *w;
2069
2070         VectorCopy(p->normal, normal);
2071         VectorVectorsDouble(normal, vright, vup);
2072
2073         VectorScale (vup, 1024.0*1024.0*1024.0, vup);
2074         VectorScale (vright, 1024.0*1024.0*1024.0, vright);
2075
2076         // project a really big axis aligned box onto the plane
2077         w = NewWinding (4);
2078
2079         VectorScale (p->normal, p->dist, org);
2080
2081         VectorSubtract (org, vright, w->points[0]);
2082         VectorAdd (w->points[0], vup, w->points[0]);
2083
2084         VectorAdd (org, vright, w->points[1]);
2085         VectorAdd (w->points[1], vup, w->points[1]);
2086
2087         VectorAdd (org, vright, w->points[2]);
2088         VectorSubtract (w->points[2], vup, w->points[2]);
2089
2090         VectorSubtract (org, vright, w->points[3]);
2091         VectorSubtract (w->points[3], vup, w->points[3]);
2092
2093         w->numpoints = 4;
2094
2095         return w;
2096 }
2097
2098 /*
2099 ==================
2100 ClipWinding
2101
2102 Clips the winding to the plane, returning the new winding on the positive side
2103 Frees the input winding.
2104 If keepon is true, an exactly on-plane winding will be saved, otherwise
2105 it will be clipped away.
2106 ==================
2107 */
2108 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
2109 {
2110         double  dists[MAX_POINTS_ON_WINDING + 1];
2111         int             sides[MAX_POINTS_ON_WINDING + 1];
2112         int             counts[3];
2113         double  dot;
2114         int             i, j;
2115         double  *p1, *p2;
2116         double  mid[3];
2117         winding_t       *neww;
2118         int             maxpts;
2119
2120         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
2121
2122         // determine sides for each point
2123         for (i = 0;i < in->numpoints;i++)
2124         {
2125                 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
2126                 if (dot > ON_EPSILON)
2127                         sides[i] = SIDE_FRONT;
2128                 else if (dot < -ON_EPSILON)
2129                         sides[i] = SIDE_BACK;
2130                 else
2131                         sides[i] = SIDE_ON;
2132                 counts[sides[i]]++;
2133         }
2134         sides[i] = sides[0];
2135         dists[i] = dists[0];
2136
2137         if (keepon && !counts[0] && !counts[1])
2138                 return in;
2139
2140         if (!counts[0])
2141         {
2142                 FreeWinding (in);
2143                 return NULL;
2144         }
2145         if (!counts[1])
2146                 return in;
2147
2148         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
2149         if (maxpts > MAX_POINTS_ON_WINDING)
2150                 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
2151
2152         neww = NewWinding (maxpts);
2153
2154         for (i = 0;i < in->numpoints;i++)
2155         {
2156                 if (neww->numpoints >= maxpts)
2157                         Sys_Error ("ClipWinding: points exceeded estimate");
2158
2159                 p1 = in->points[i];
2160
2161                 if (sides[i] == SIDE_ON)
2162                 {
2163                         VectorCopy (p1, neww->points[neww->numpoints]);
2164                         neww->numpoints++;
2165                         continue;
2166                 }
2167
2168                 if (sides[i] == SIDE_FRONT)
2169                 {
2170                         VectorCopy (p1, neww->points[neww->numpoints]);
2171                         neww->numpoints++;
2172                 }
2173
2174                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2175                         continue;
2176
2177                 // generate a split point
2178                 p2 = in->points[(i+1)%in->numpoints];
2179
2180                 dot = dists[i] / (dists[i]-dists[i+1]);
2181                 for (j = 0;j < 3;j++)
2182                 {       // avoid round off error when possible
2183                         if (split->normal[j] == 1)
2184                                 mid[j] = split->dist;
2185                         else if (split->normal[j] == -1)
2186                                 mid[j] = -split->dist;
2187                         else
2188                                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
2189                 }
2190
2191                 VectorCopy (mid, neww->points[neww->numpoints]);
2192                 neww->numpoints++;
2193         }
2194
2195         // free the original winding
2196         FreeWinding (in);
2197
2198         return neww;
2199 }
2200
2201
2202 /*
2203 ==================
2204 DivideWinding
2205
2206 Divides a winding by a plane, producing one or two windings.  The
2207 original winding is not damaged or freed.  If only on one side, the
2208 returned winding will be the input winding.  If on both sides, two
2209 new windings will be created.
2210 ==================
2211 */
2212 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
2213 {
2214         double  dists[MAX_POINTS_ON_WINDING + 1];
2215         int             sides[MAX_POINTS_ON_WINDING + 1];
2216         int             counts[3];
2217         double  dot;
2218         int             i, j;
2219         double  *p1, *p2;
2220         double  mid[3];
2221         winding_t       *f, *b;
2222         int             maxpts;
2223
2224         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
2225
2226         // determine sides for each point
2227         for (i = 0;i < in->numpoints;i++)
2228         {
2229                 dot = DotProduct (in->points[i], split->normal);
2230                 dot -= split->dist;
2231                 dists[i] = dot;
2232                 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
2233                 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
2234                 else sides[i] = SIDE_ON;
2235                 counts[sides[i]]++;
2236         }
2237         sides[i] = sides[0];
2238         dists[i] = dists[0];
2239
2240         *front = *back = NULL;
2241
2242         if (!counts[0])
2243         {
2244                 *back = in;
2245                 return;
2246         }
2247         if (!counts[1])
2248         {
2249                 *front = in;
2250                 return;
2251         }
2252
2253         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
2254
2255         if (maxpts > MAX_POINTS_ON_WINDING)
2256                 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
2257
2258         *front = f = NewWinding (maxpts);
2259         *back = b = NewWinding (maxpts);
2260
2261         for (i = 0;i < in->numpoints;i++)
2262         {
2263                 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
2264                         Sys_Error ("DivideWinding: points exceeded estimate");
2265
2266                 p1 = in->points[i];
2267
2268                 if (sides[i] == SIDE_ON)
2269                 {
2270                         VectorCopy (p1, f->points[f->numpoints]);
2271                         f->numpoints++;
2272                         VectorCopy (p1, b->points[b->numpoints]);
2273                         b->numpoints++;
2274                         continue;
2275                 }
2276
2277                 if (sides[i] == SIDE_FRONT)
2278                 {
2279                         VectorCopy (p1, f->points[f->numpoints]);
2280                         f->numpoints++;
2281                 }
2282                 else if (sides[i] == SIDE_BACK)
2283                 {
2284                         VectorCopy (p1, b->points[b->numpoints]);
2285                         b->numpoints++;
2286                 }
2287
2288                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2289                         continue;
2290
2291                 // generate a split point
2292                 p2 = in->points[(i+1)%in->numpoints];
2293
2294                 dot = dists[i] / (dists[i]-dists[i+1]);
2295                 for (j = 0;j < 3;j++)
2296                 {       // avoid round off error when possible
2297                         if (split->normal[j] == 1)
2298                                 mid[j] = split->dist;
2299                         else if (split->normal[j] == -1)
2300                                 mid[j] = -split->dist;
2301                         else
2302                                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
2303                 }
2304
2305                 VectorCopy (mid, f->points[f->numpoints]);
2306                 f->numpoints++;
2307                 VectorCopy (mid, b->points[b->numpoints]);
2308                 b->numpoints++;
2309         }
2310 }
2311
2312 typedef struct portal_s
2313 {
2314         mplane_t plane;
2315         mnode_t *nodes[2];              // [0] = front side of plane
2316         struct portal_s *next[2];
2317         winding_t *winding;
2318         struct portal_s *chain; // all portals are linked into a list
2319 }
2320 portal_t;
2321
2322 static portal_t *portalchain;
2323
2324 /*
2325 ===========
2326 AllocPortal
2327 ===========
2328 */
2329 static portal_t *AllocPortal (void)
2330 {
2331         portal_t *p;
2332         p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2333         p->chain = portalchain;
2334         portalchain = p;
2335         return p;
2336 }
2337
2338 static void FreePortal(portal_t *p)
2339 {
2340         Mem_Free(p);
2341 }
2342
2343 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
2344 {
2345         // calculate children first
2346         if (node->children[0]->contents >= 0)
2347                 Mod_RecursiveRecalcNodeBBox(node->children[0]);
2348         if (node->children[1]->contents >= 0)
2349                 Mod_RecursiveRecalcNodeBBox(node->children[1]);
2350
2351         // make combined bounding box from children
2352         node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2353         node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2354         node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2355         node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2356         node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2357         node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2358 }
2359
2360 static void Mod_FinalizePortals(void)
2361 {
2362         int i, j, numportals, numpoints;
2363         portal_t *p, *pnext;
2364         mportal_t *portal;
2365         mvertex_t *point;
2366         mleaf_t *leaf, *endleaf;
2367         winding_t *w;
2368
2369         // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
2370         leaf = loadmodel->leafs;
2371         endleaf = leaf + loadmodel->numleafs;
2372         for (;leaf < endleaf;leaf++)
2373         {
2374                 VectorSet(leaf->mins,  2000000000,  2000000000,  2000000000);
2375                 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2376         }
2377         p = portalchain;
2378         while(p)
2379         {
2380                 if (p->winding)
2381                 {
2382                         for (i = 0;i < 2;i++)
2383                         {
2384                                 leaf = (mleaf_t *)p->nodes[i];
2385                                 w = p->winding;
2386                                 for (j = 0;j < w->numpoints;j++)
2387                                 {
2388                                         if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2389                                         if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2390                                         if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2391                                         if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2392                                         if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2393                                         if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2394                                 }
2395                         }
2396                 }
2397                 p = p->chain;
2398         }
2399
2400         Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2401
2402         // tally up portal and point counts
2403         p = portalchain;
2404         numportals = 0;
2405         numpoints = 0;
2406         while(p)
2407         {
2408                 // note: this check must match the one below or it will usually corrupt memory
2409                 // 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
2410                 if (p->winding && p->nodes[0] != p->nodes[1]
2411                  && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2412                  && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2413                 {
2414                         numportals += 2;
2415                         numpoints += p->winding->numpoints * 2;
2416                 }
2417                 p = p->chain;
2418         }
2419         loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2420         loadmodel->numportals = numportals;
2421         loadmodel->portalpoints = (void *) ((qbyte *) loadmodel->portals + numportals * sizeof(mportal_t));
2422         loadmodel->numportalpoints = numpoints;
2423         // clear all leaf portal chains
2424         for (i = 0;i < loadmodel->numleafs;i++)
2425                 loadmodel->leafs[i].portals = NULL;
2426         // process all portals in the global portal chain, while freeing them
2427         portal = loadmodel->portals;
2428         point = loadmodel->portalpoints;
2429         p = portalchain;
2430         portalchain = NULL;
2431         while (p)
2432         {
2433                 pnext = p->chain;
2434
2435                 if (p->winding)
2436                 {
2437                         // note: this check must match the one above or it will usually corrupt memory
2438                         // 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
2439                         if (p->nodes[0] != p->nodes[1]
2440                          && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2441                          && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2442                         {
2443                                 // first make the back to front portal (forward portal)
2444                                 portal->points = point;
2445                                 portal->numpoints = p->winding->numpoints;
2446                                 portal->plane.dist = p->plane.dist;
2447                                 VectorCopy(p->plane.normal, portal->plane.normal);
2448                                 portal->here = (mleaf_t *)p->nodes[1];
2449                                 portal->past = (mleaf_t *)p->nodes[0];
2450                                 // copy points
2451                                 for (j = 0;j < portal->numpoints;j++)
2452                                 {
2453                                         VectorCopy(p->winding->points[j], point->position);
2454                                         point++;
2455                                 }
2456                                 PlaneClassify(&portal->plane);
2457
2458                                 // link into leaf's portal chain
2459                                 portal->next = portal->here->portals;
2460                                 portal->here->portals = portal;
2461
2462                                 // advance to next portal
2463                                 portal++;
2464
2465                                 // then make the front to back portal (backward portal)
2466                                 portal->points = point;
2467                                 portal->numpoints = p->winding->numpoints;
2468                                 portal->plane.dist = -p->plane.dist;
2469                                 VectorNegate(p->plane.normal, portal->plane.normal);
2470                                 portal->here = (mleaf_t *)p->nodes[0];
2471                                 portal->past = (mleaf_t *)p->nodes[1];
2472                                 // copy points
2473                                 for (j = portal->numpoints - 1;j >= 0;j--)
2474                                 {
2475                                         VectorCopy(p->winding->points[j], point->position);
2476                                         point++;
2477                                 }
2478                                 PlaneClassify(&portal->plane);
2479
2480                                 // link into leaf's portal chain
2481                                 portal->next = portal->here->portals;
2482                                 portal->here->portals = portal;
2483
2484                                 // advance to next portal
2485                                 portal++;
2486                         }
2487                         FreeWinding(p->winding);
2488                 }
2489                 FreePortal(p);
2490                 p = pnext;
2491         }
2492 }
2493
2494 /*
2495 =============
2496 AddPortalToNodes
2497 =============
2498 */
2499 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2500 {
2501         if (!front)
2502                 Host_Error ("AddPortalToNodes: NULL front node");
2503         if (!back)
2504                 Host_Error ("AddPortalToNodes: NULL back node");
2505         if (p->nodes[0] || p->nodes[1])
2506                 Host_Error ("AddPortalToNodes: already included");
2507         // 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
2508
2509         p->nodes[0] = front;
2510         p->next[0] = (portal_t *)front->portals;
2511         front->portals = (mportal_t *)p;
2512
2513         p->nodes[1] = back;
2514         p->next[1] = (portal_t *)back->portals;
2515         back->portals = (mportal_t *)p;
2516 }
2517
2518 /*
2519 =============
2520 RemovePortalFromNode
2521 =============
2522 */
2523 static void RemovePortalFromNodes(portal_t *portal)
2524 {
2525         int i;
2526         mnode_t *node;
2527         void **portalpointer;
2528         portal_t *t;
2529         for (i = 0;i < 2;i++)
2530         {
2531                 node = portal->nodes[i];
2532
2533                 portalpointer = (void **) &node->portals;
2534                 while (1)
2535                 {
2536                         t = *portalpointer;
2537                         if (!t)
2538                                 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2539
2540                         if (t == portal)
2541                         {
2542                                 if (portal->nodes[0] == node)
2543                                 {
2544                                         *portalpointer = portal->next[0];
2545                                         portal->nodes[0] = NULL;
2546                                 }
2547                                 else if (portal->nodes[1] == node)
2548                                 {
2549                                         *portalpointer = portal->next[1];
2550                                         portal->nodes[1] = NULL;
2551                                 }
2552                                 else
2553                                         Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2554                                 break;
2555                         }
2556
2557                         if (t->nodes[0] == node)
2558                                 portalpointer = (void **) &t->next[0];
2559                         else if (t->nodes[1] == node)
2560                                 portalpointer = (void **) &t->next[1];
2561                         else
2562                                 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2563                 }
2564         }
2565 }
2566
2567 static void Mod_RecursiveNodePortals (mnode_t *node)
2568 {
2569         int side;
2570         mnode_t *front, *back, *other_node;
2571         mplane_t clipplane, *plane;
2572         portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2573         winding_t *nodeportalwinding, *frontwinding, *backwinding;
2574
2575         // if a leaf, we're done
2576         if (node->contents)
2577                 return;
2578
2579         plane = node->plane;
2580
2581         front = node->children[0];
2582         back = node->children[1];
2583         if (front == back)
2584                 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2585
2586         // create the new portal by generating a polygon for the node plane,
2587         // and clipping it by all of the other portals (which came from nodes above this one)
2588         nodeportal = AllocPortal ();
2589         nodeportal->plane = *node->plane;
2590
2591         nodeportalwinding = BaseWindingForPlane (node->plane);
2592         side = 0;       // shut up compiler warning
2593         for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2594         {
2595                 clipplane = portal->plane;
2596                 if (portal->nodes[0] == portal->nodes[1])
2597                         Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2598                 if (portal->nodes[0] == node)
2599                         side = 0;
2600                 else if (portal->nodes[1] == node)
2601                 {
2602                         clipplane.dist = -clipplane.dist;
2603                         VectorNegate (clipplane.normal, clipplane.normal);
2604                         side = 1;
2605                 }
2606                 else
2607                         Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2608
2609                 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2610                 if (!nodeportalwinding)
2611                 {
2612                         printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2613                         break;
2614                 }
2615         }
2616
2617         if (nodeportalwinding)
2618         {
2619                 // if the plane was not clipped on all sides, there was an error
2620                 nodeportal->winding = nodeportalwinding;
2621                 AddPortalToNodes (nodeportal, front, back);
2622         }
2623
2624         // split the portals of this node along this node's plane and assign them to the children of this node
2625         // (migrating the portals downward through the tree)
2626         for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2627         {
2628                 if (portal->nodes[0] == portal->nodes[1])
2629                         Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2630                 if (portal->nodes[0] == node)
2631                         side = 0;
2632                 else if (portal->nodes[1] == node)
2633                         side = 1;
2634                 else
2635                         Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2636                 nextportal = portal->next[side];
2637
2638                 other_node = portal->nodes[!side];
2639                 RemovePortalFromNodes (portal);
2640
2641                 // cut the portal into two portals, one on each side of the node plane
2642                 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2643
2644                 if (!frontwinding)
2645                 {
2646                         if (side == 0)
2647                                 AddPortalToNodes (portal, back, other_node);
2648                         else
2649                                 AddPortalToNodes (portal, other_node, back);
2650                         continue;
2651                 }
2652                 if (!backwinding)
2653                 {
2654                         if (side == 0)
2655                                 AddPortalToNodes (portal, front, other_node);
2656                         else
2657                                 AddPortalToNodes (portal, other_node, front);
2658                         continue;
2659                 }
2660
2661                 // the winding is split
2662                 splitportal = AllocPortal ();
2663                 temp = splitportal->chain;
2664                 *splitportal = *portal;
2665                 splitportal->chain = temp;
2666                 splitportal->winding = backwinding;
2667                 FreeWinding (portal->winding);
2668                 portal->winding = frontwinding;
2669
2670                 if (side == 0)
2671                 {
2672                         AddPortalToNodes (portal, front, other_node);
2673                         AddPortalToNodes (splitportal, back, other_node);
2674                 }
2675                 else
2676                 {
2677                         AddPortalToNodes (portal, other_node, front);
2678                         AddPortalToNodes (splitportal, other_node, back);
2679                 }
2680         }
2681
2682         Mod_RecursiveNodePortals(front);
2683         Mod_RecursiveNodePortals(back);
2684 }
2685
2686
2687 static void Mod_MakePortals(void)
2688 {
2689         portalchain = NULL;
2690         Mod_RecursiveNodePortals (loadmodel->nodes);
2691         Mod_FinalizePortals();
2692 }
2693
2694 static void Mod_BuildSurfaceNeighbors (msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2695 {
2696 #if 0
2697         int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
2698         msurface_t *surf, *s;
2699         float *v0, *v1, *v2, *v3;
2700         for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2701                 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2702         for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2703         {
2704                 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)
2705                 {
2706                         if (surf->neighborsurfaces[vertnum])
2707                                 continue;
2708                         surf->neighborsurfaces[vertnum] = NULL;
2709                         for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2710                         {
2711                                 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2712                                  || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2713                                  || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2714                                  || s == surf)
2715                                         continue;
2716                                 for (vnum = 0;vnum < s->poly_numverts;vnum++)
2717                                         if (s->neighborsurfaces[vnum] == surf)
2718                                                 break;
2719                                 if (vnum < s->poly_numverts)
2720                                         continue;
2721                                 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)
2722                                 {
2723                                         if (s->neighborsurfaces[vnum] == NULL
2724                                          && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2725                                           || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
2726                                         {
2727                                                 surf->neighborsurfaces[vertnum] = s;
2728                                                 s->neighborsurfaces[vnum] = surf;
2729                                                 break;
2730                                         }
2731                                 }
2732                                 if (vnum < s->poly_numverts)
2733                                         break;
2734                         }
2735                 }
2736         }
2737 #endif
2738 }
2739
2740 /*
2741 =================
2742 Mod_LoadBrushModel
2743 =================
2744 */
2745 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
2746 extern void R_Model_Brush_Draw(entity_render_t *ent);
2747 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
2748 extern void R_Model_Brush_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor);
2749 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2750 {
2751         int                     i, j;
2752         dheader_t       *header;
2753         dmodel_t        *bm;
2754         mempool_t       *mainmempool;
2755         char            *loadname;
2756         model_t         *originalloadmodel;
2757
2758         mod->type = mod_brush;
2759
2760         header = (dheader_t *)buffer;
2761
2762         i = LittleLong (header->version);
2763         if (i != BSPVERSION && i != 30)
2764                 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i (Quake) or 30 (HalfLife))", mod->name, i, BSPVERSION);
2765         mod->ishlbsp = i == 30;
2766         if (loadmodel->isworldmodel)
2767         {
2768                 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2769                 // until we get a texture for it...
2770                 R_ResetQuakeSky();
2771         }
2772
2773 // swap all the lumps
2774         mod_base = (qbyte *)header;
2775
2776         for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
2777                 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
2778
2779 // load into heap
2780
2781         // store which lightmap format to use
2782         mod->lightmaprgba = r_lightmaprgba.integer;
2783
2784         Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2785         Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
2786         Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
2787         Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
2788         Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
2789         Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
2790         Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
2791         Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
2792         Mod_LoadFaces (&header->lumps[LUMP_FACES]);
2793         Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
2794         Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
2795         Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
2796         Mod_LoadNodes (&header->lumps[LUMP_NODES]);
2797         Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
2798         Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
2799
2800         Mod_MakeHull0 ();
2801         Mod_MakePortals();
2802
2803         mod->numframes = 2;             // regular and alternate animation
2804
2805         mainmempool = mod->mempool;
2806         loadname = mod->name;
2807
2808         Mod_LoadLightList ();
2809         originalloadmodel = loadmodel;
2810
2811 //
2812 // set up the submodels (FIXME: this is confusing)
2813 //
2814         for (i = 0;i < mod->numsubmodels;i++)
2815         {
2816                 int k, l;
2817                 float dist, modelyawradius, modelradius, *vec;
2818                 msurface_t *surf;
2819
2820                 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2821                 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2822                 modelyawradius = 0;
2823                 modelradius = 0;
2824
2825                 bm = &mod->submodels[i];
2826
2827                 mod->hulls[0].firstclipnode = bm->headnode[0];
2828                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2829                 {
2830                         mod->hulls[j].firstclipnode = bm->headnode[j];
2831                         mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2832                 }
2833
2834                 mod->firstmodelsurface = bm->firstface;
2835                 mod->nummodelsurfaces = bm->numfaces;
2836
2837                 // this gets altered below if sky is used
2838                 mod->DrawSky = NULL;
2839                 mod->Draw = R_Model_Brush_Draw;
2840                 mod->DrawFakeShadow = NULL;
2841                 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
2842                 mod->DrawLight = R_Model_Brush_DrawLight;
2843                 mod->texturesurfacechains = Mem_Alloc(originalloadmodel->mempool, mod->numtextures * sizeof(msurface_t *));
2844                 if (mod->nummodelsurfaces)
2845                 {
2846                         // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2847                         for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2848                         {
2849                                 // we only need to have a drawsky function if it is used (usually only on world model)
2850                                 if (surf->texinfo->texture->shader == &Cshader_sky)
2851                                         mod->DrawSky = R_Model_Brush_DrawSky;
2852                                 // link into texture chain
2853                                 surf->texturechain = mod->texturesurfacechains[surf->texinfo->texture - mod->textures];
2854                                 mod->texturesurfacechains[surf->texinfo->texture - mod->textures] = surf;
2855                                 // calculate bounding shapes
2856                                 for (k = 0;k < surf->numedges;k++)
2857                                 {
2858                                         l = mod->surfedges[k + surf->firstedge];
2859                                         if (l > 0)
2860                                                 vec = mod->vertexes[mod->edges[l].v[0]].position;
2861                                         else
2862                                                 vec = mod->vertexes[mod->edges[-l].v[1]].position;
2863                                         if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2864                                         if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2865                                         if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2866                                         if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2867                                         if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2868                                         if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2869                                         dist = vec[0]*vec[0]+vec[1]*vec[1];
2870                                         if (modelyawradius < dist)
2871                                                 modelyawradius = dist;
2872                                         dist += vec[2]*vec[2];
2873                                         if (modelradius < dist)
2874                                                 modelradius = dist;
2875                                 }
2876                         }
2877                         modelyawradius = sqrt(modelyawradius);
2878                         modelradius = sqrt(modelradius);
2879                         mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2880                         mod->yawmins[2] = mod->normalmins[2];
2881                         mod->yawmaxs[2] = mod->normalmaxs[2];
2882                         mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2883                         mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2884                         mod->radius = modelradius;
2885                         mod->radius2 = modelradius * modelradius;
2886                         // LordHavoc: build triangle meshs for entire model's geometry
2887                         // (only used for shadow volumes)
2888                         mod->shadowmesh = Mod_ShadowMesh_Begin(originalloadmodel->mempool, 1024);
2889                         for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2890                                 if (surf->flags & SURF_SHADOWCAST)
2891                                         Mod_ShadowMesh_AddPolygon(originalloadmodel->mempool, mod->shadowmesh, surf->poly_numverts, surf->poly_verts);
2892                         mod->shadowmesh = Mod_ShadowMesh_Finish(originalloadmodel->mempool, mod->shadowmesh);
2893                         Mod_ShadowMesh_CalcBBox(mod->shadowmesh, mod->shadowmesh_mins, mod->shadowmesh_maxs, mod->shadowmesh_center, &mod->shadowmesh_radius);
2894                 }
2895                 else
2896                 {
2897                         // LordHavoc: empty submodel (lacrima.bsp has such a glitch)
2898                         Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2899                         VectorClear(mod->normalmins);
2900                         VectorClear(mod->normalmaxs);
2901                         VectorClear(mod->yawmins);
2902                         VectorClear(mod->yawmaxs);
2903                         VectorClear(mod->rotatedmins);
2904                         VectorClear(mod->rotatedmaxs);
2905                         mod->radius = 0;
2906                         mod->radius2 = 0;
2907                         mod->shadowmesh = NULL;
2908                 }
2909                 Mod_BuildSurfaceNeighbors(mod->surfaces + mod->firstmodelsurface, mod->nummodelsurfaces, originalloadmodel->mempool);
2910
2911                 mod->numleafs = bm->visleafs;
2912
2913                 // LordHavoc: only register submodels if it is the world
2914                 // (prevents bsp models from replacing world submodels)
2915                 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2916                 {
2917                         char    name[10];
2918                         // duplicate the basic information
2919                         sprintf (name, "*%i", i+1);
2920                         loadmodel = Mod_FindName (name);
2921                         *loadmodel = *mod;
2922                         strcpy (loadmodel->name, name);
2923                         // textures and memory belong to the main model
2924                         loadmodel->texturepool = NULL;
2925                         loadmodel->mempool = NULL;
2926                         mod = loadmodel;
2927                 }
2928         }
2929
2930         loadmodel = originalloadmodel;
2931         //Mod_ProcessLightList ();
2932 }
2933